mirror of
https://gitlab.com/gaelysam/mapgen_rivers.git
synced 2025-07-03 00:40:38 +02:00
Compare commits
5 Commits
fix_rivers
...
mapgen_thr
Author | SHA1 | Date | |
---|---|---|---|
e9fa7f9a5c | |||
f26fd1cccb | |||
e5b8f2b3b8 | |||
6fa1852277 | |||
11e6e72324 |
@ -1,10 +1,10 @@
|
||||
local modpath = mapgen_rivers.modpath
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
|
||||
local make_polygons = dofile(modpath .. 'polygons.lua')
|
||||
local transform_quadri = dofile(modpath .. 'geometry.lua')
|
||||
local make_polygons = dofile(modpath .. '/polygons.lua')
|
||||
local transform_quadri = dofile(modpath .. '/geometry.lua')
|
||||
|
||||
local sea_level = mapgen_rivers.settings.sea_level
|
||||
local riverbed_slope = mapgen_rivers.settings.riverbed_slope * mapgen_rivers.settings.blocksize
|
||||
local sea_level = tonumber(minetest.get_mapgen_setting("water_level"))
|
||||
local riverbed_slope = tonumber(mapgen_rivers.settings:get("riverbed_slope")) * tonumber(mapgen_rivers.settings:get("blocksize"))
|
||||
|
||||
local MAP_BOTTOM = -31000
|
||||
|
||||
@ -46,57 +46,50 @@ local function heightmaps(minp, maxp)
|
||||
-- = 0: on riverbank
|
||||
-- > 0: inside river
|
||||
local depth_factors = {
|
||||
r_west - xf , -- West edge (1)
|
||||
r_north - zf , -- North edge (2)
|
||||
r_east - (1-xf), -- East edge (3)
|
||||
r_south - (1-zf), -- South edge (4)
|
||||
c_NW - xf - zf , -- North-West corner (5)
|
||||
c_NE - (1-xf) - zf , -- North-East corner (6)
|
||||
c_SE - (1-xf) - (1-zf), -- South-East corner (7)
|
||||
c_SW - xf - (1-zf), -- South-West corner (8)
|
||||
r_west - xf,
|
||||
r_north - zf,
|
||||
xf - r_east,
|
||||
zf - r_south,
|
||||
c_NW-xf-zf,
|
||||
xf-zf-c_NE,
|
||||
xf+zf-c_SE,
|
||||
zf-xf-c_SW,
|
||||
}
|
||||
|
||||
-- Find the maximal depth factor, which determines to which of the 8 river sections (4 edges + 4 corners) the current point belongs.
|
||||
-- If imax is still at 0, it means that we are not in a river.
|
||||
local dpmax = 0
|
||||
-- Find the maximal depth factor and determine to which river it belongs
|
||||
local depth_factor_max = 0
|
||||
local imax = 0
|
||||
for i=1, 8 do
|
||||
if depth_factors[i] > dpmax then
|
||||
dpmax = depth_factors[i]
|
||||
if depth_factors[i] >= depth_factor_max then
|
||||
depth_factor_max = depth_factors[i]
|
||||
imax = i
|
||||
end
|
||||
end
|
||||
|
||||
-- Transform the coordinates to have xfc and zfc = 0 or 1 in rivers (to avoid rivers having lateral slope and to accomodate the riverbanks smoothly)
|
||||
local xfc, zfc
|
||||
-- xfc:
|
||||
if imax == 0 or imax == 2 or imax == 4 then -- river segment does not constrain X coordinate, so accomodate xf in function of other river sections
|
||||
local x0 = max(r_west-dpmax, c_NW-zf-dpmax, c_SW-(1-zf)-dpmax, 0) -- new xf will be bounded to 0 by western riverbank
|
||||
local x1 = 1-max(r_east-dpmax, c_NE-zf-dpmax, c_SE-(1-zf)-dpmax, 0) -- and bounded to 1 by eastern riverbank
|
||||
if x0 >= x1 then
|
||||
xfc = 0.5
|
||||
else
|
||||
xfc = (xf-x0) / (x1-x0)
|
||||
end
|
||||
elseif imax == 1 or imax == 5 or imax == 8 then -- river at the western side of the polygon
|
||||
xfc = 0
|
||||
else -- 3, 6, 7 : river at the eastern side of the polygon
|
||||
xfc = 1
|
||||
end
|
||||
|
||||
-- Same for zfc:
|
||||
if imax == 0 or imax == 1 or imax == 3 then -- river segment does not constrain Z coordinate, so accomodate zf in function of other river sections
|
||||
local z0 = max(r_north-dpmax, c_NW-xf-dpmax, c_NE-(1-xf)-dpmax, 0) -- new zf will be bounded to 0 by northern riverbank
|
||||
local z1 = 1-max(r_south-dpmax, c_SW-xf-dpmax, c_SE-(1-xf)-dpmax, 0) -- and bounded to 1 by southern riverbank
|
||||
if z0 >= z1 then
|
||||
zfc = 0.5
|
||||
else
|
||||
zfc = (zf-z0) / (z1-z0)
|
||||
end
|
||||
elseif imax == 2 or imax == 5 or imax == 6 then -- river at the northern side of the polygon
|
||||
zfc = 0
|
||||
else -- 4, 7, 8 : river at the southern side of the polygon
|
||||
zfc = 1
|
||||
-- Transform the coordinates to have xf and zf = 0 or 1 in rivers (to avoid rivers having lateral slope and to accomodate the surrounding smoothly)
|
||||
if imax == 0 then
|
||||
local x0 = max(r_west, c_NW-zf, zf-c_SW)
|
||||
local x1 = min(r_east, c_NE+zf, c_SE-zf)
|
||||
local z0 = max(r_north, c_NW-xf, xf-c_NE)
|
||||
local z1 = min(r_south, c_SW+xf, c_SE-xf)
|
||||
xf = (xf-x0) / (x1-x0)
|
||||
zf = (zf-z0) / (z1-z0)
|
||||
elseif imax == 1 then
|
||||
xf = 0
|
||||
elseif imax == 2 then
|
||||
zf = 0
|
||||
elseif imax == 3 then
|
||||
xf = 1
|
||||
elseif imax == 4 then
|
||||
zf = 1
|
||||
elseif imax == 5 then
|
||||
xf, zf = 0, 0
|
||||
elseif imax == 6 then
|
||||
xf, zf = 1, 0
|
||||
elseif imax == 7 then
|
||||
xf, zf = 1, 1
|
||||
elseif imax == 8 then
|
||||
xf, zf = 0, 1
|
||||
end
|
||||
|
||||
-- Determine elevation by interpolation
|
||||
@ -106,12 +99,12 @@ local function heightmaps(minp, maxp)
|
||||
vdem[2],
|
||||
vdem[3],
|
||||
vdem[4],
|
||||
xfc, zfc
|
||||
xf, zf
|
||||
))
|
||||
|
||||
-- Spatial gradient of the interpolation
|
||||
local slope_x = zfc*(vdem[3]-vdem[4]) + (1-zfc)*(vdem[2]-vdem[1]) < 0
|
||||
local slope_z = xfc*(vdem[3]-vdem[2]) + (1-xfc)*(vdem[4]-vdem[1]) < 0
|
||||
local slope_x = zf*(vdem[3]-vdem[4]) + (1-zf)*(vdem[2]-vdem[1]) < 0
|
||||
local slope_z = xf*(vdem[3]-vdem[2]) + (1-xf)*(vdem[4]-vdem[1]) < 0
|
||||
local lake_id = 0
|
||||
if slope_x then
|
||||
if slope_z then
|
||||
@ -128,8 +121,8 @@ local function heightmaps(minp, maxp)
|
||||
end
|
||||
local lake_height = max(floor(poly.lake[lake_id]), terrain_height)
|
||||
|
||||
if imax > 0 and dpmax > 0 then
|
||||
terrain_height = min(max(lake_height, sea_level) - floor(1+dpmax*riverbed_slope), terrain_height)
|
||||
if imax > 0 and depth_factor_max > 0 then
|
||||
terrain_height = min(max(lake_height, sea_level) - floor(1+depth_factor_max*riverbed_slope), terrain_height)
|
||||
end
|
||||
|
||||
terrain_height_map[i] = terrain_height
|
||||
|
294
init.lua
294
init.lua
@ -1,279 +1,43 @@
|
||||
mapgen_rivers = {}
|
||||
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. '/'
|
||||
mapgen_rivers.modpath = modpath
|
||||
mapgen_rivers.world_data_path = minetest.get_worldpath() .. '/river_data/'
|
||||
|
||||
if minetest.get_mapgen_setting("mg_name") ~= "singlenode" then
|
||||
minetest.set_mapgen_setting("mg_name", "singlenode", true)
|
||||
minetest.log("warning", "[mapgen_rivers] Mapgen set to singlenode")
|
||||
end
|
||||
|
||||
dofile(modpath .. 'settings.lua')
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
dofile(modpath .. '/settings.lua')
|
||||
|
||||
local sea_level = mapgen_rivers.settings.sea_level
|
||||
local elevation_chill = mapgen_rivers.settings.elevation_chill
|
||||
local use_distort = mapgen_rivers.settings.distort
|
||||
local use_biomes = mapgen_rivers.settings.biomes
|
||||
local use_biomegen_mod = use_biomes and minetest.global_exists('biomegen')
|
||||
use_biomes = use_biomes and minetest.global_exists('default') and not use_biomegen_mod
|
||||
|
||||
if use_biomegen_mod then
|
||||
biomegen.set_elevation_chill(elevation_chill)
|
||||
end
|
||||
dofile(modpath .. 'noises.lua')
|
||||
|
||||
local heightmaps = dofile(modpath .. 'heightmap.lua')
|
||||
|
||||
-- Linear interpolation
|
||||
local function interp(v00, v01, v11, v10, xf, zf)
|
||||
local v0 = v01*xf + v00*(1-xf)
|
||||
local v1 = v11*xf + v10*(1-xf)
|
||||
return v1*zf + v0*(1-zf)
|
||||
local sfile = io.open(minetest.get_worldpath() .. '/river_data/size')
|
||||
if sfile then
|
||||
sfile:close()
|
||||
else
|
||||
dofile(modpath .. '/pregenerate.lua')
|
||||
collectgarbage()
|
||||
end
|
||||
|
||||
-- Localize for performance
|
||||
local floor, min = math.floor, math.min
|
||||
|
||||
local data = {}
|
||||
|
||||
local noise_x_obj, noise_z_obj, noise_distort_obj, noise_heat_obj, noise_heat_blend_obj
|
||||
local noise_x_map = {}
|
||||
local noise_z_map = {}
|
||||
local noise_distort_map = {}
|
||||
local noise_heat_map = {}
|
||||
local noise_heat_blend_map = {}
|
||||
local mapsize
|
||||
local init = false
|
||||
|
||||
local sumtime = 0
|
||||
local sumtime2 = 0
|
||||
local ngen = 0
|
||||
|
||||
local function generate(minp, maxp, seed)
|
||||
minetest.log("info", ("[mapgen_rivers] Generating from %s to %s"):format(minetest.pos_to_string(minp), minetest.pos_to_string(maxp)))
|
||||
|
||||
local chulens = {
|
||||
x = maxp.x-minp.x+1,
|
||||
y = maxp.y-minp.y+1,
|
||||
z = maxp.z-minp.z+1,
|
||||
}
|
||||
|
||||
if not init then
|
||||
mapsize = {
|
||||
x = chulens.x,
|
||||
y = chulens.y+1,
|
||||
z = chulens.z,
|
||||
}
|
||||
if use_distort then
|
||||
noise_x_obj = minetest.get_perlin_map(mapgen_rivers.noise_params.distort_x, mapsize)
|
||||
noise_z_obj = minetest.get_perlin_map(mapgen_rivers.noise_params.distort_z, mapsize)
|
||||
noise_distort_obj = minetest.get_perlin_map(mapgen_rivers.noise_params.distort_amplitude, chulens)
|
||||
end
|
||||
if use_biomes then
|
||||
noise_heat_obj = minetest.get_perlin_map(mapgen_rivers.noise_params.heat, chulens)
|
||||
noise_heat_blend_obj = minetest.get_perlin_map(mapgen_rivers.noise_params.heat_blend, chulens)
|
||||
end
|
||||
init = true
|
||||
end
|
||||
|
||||
local t0 = os.clock()
|
||||
local minp2d = {x=minp.x, y=minp.z}
|
||||
if use_distort then
|
||||
noise_x_obj:get_3d_map_flat(minp, noise_x_map)
|
||||
noise_z_obj:get_3d_map_flat(minp, noise_z_map)
|
||||
noise_distort_obj:get_2d_map_flat(minp2d, noise_distort_map)
|
||||
end
|
||||
if use_biomes then
|
||||
noise_heat_obj:get_2d_map_flat(minp2d, noise_heat_map)
|
||||
noise_heat_blend_obj:get_2d_map_flat(minp2d, noise_heat_blend_map)
|
||||
end
|
||||
|
||||
local terrain_map, lake_map, incr, i_origin
|
||||
|
||||
if use_distort then
|
||||
local xmin, xmax, zmin, zmax = minp.x, maxp.x, minp.z, maxp.z
|
||||
local i = 0
|
||||
local i2d = 0
|
||||
for z=minp.z, maxp.z do
|
||||
for y=minp.y, maxp.y+1 do
|
||||
for x=minp.x, maxp.x do
|
||||
i = i+1
|
||||
i2d = i2d+1
|
||||
local distort = noise_distort_map[i2d]
|
||||
local xv = noise_x_map[i]*distort + x
|
||||
if xv < xmin then xmin = xv end
|
||||
if xv > xmax then xmax = xv end
|
||||
noise_x_map[i] = xv
|
||||
local zv = noise_z_map[i]*distort + z
|
||||
if zv < zmin then zmin = zv end
|
||||
if zv > zmax then zmax = zv end
|
||||
noise_z_map[i] = zv
|
||||
end
|
||||
i2d = i2d-chulens.x
|
||||
end
|
||||
end
|
||||
|
||||
local pminp = {x=floor(xmin), z=floor(zmin)}
|
||||
local pmaxp = {x=floor(xmax)+1, z=floor(zmax)+1}
|
||||
incr = pmaxp.x-pminp.x+1
|
||||
i_origin = 1 - pminp.z*incr - pminp.x
|
||||
terrain_map, lake_map = heightmaps(pminp, pmaxp)
|
||||
mapgen_rivers.use_mapgen_thread = minetest.settings:get_bool("mapgen_rivers_use_mapgen_thread")
|
||||
mapgen_rivers.thread = 'main'
|
||||
if mapgen_rivers.use_mapgen_thread then
|
||||
if minetest.register_mapgen_dofile then
|
||||
minetest.register_mapgen_dofile(modpath .. '/mapgen.lua')
|
||||
else
|
||||
terrain_map, lake_map = heightmaps(minp, maxp)
|
||||
minetest.log("warning", "[mapgen_rivers] Mapgen thread not available on this Minetest version.")
|
||||
mapgen_rivers.use_mapgen_thread = false
|
||||
end
|
||||
|
||||
-- Check that there is at least one position that reaches min y
|
||||
if minp.y > sea_level then
|
||||
local y0 = minp.y
|
||||
local is_empty = true
|
||||
for i=1, #terrain_map do
|
||||
if terrain_map[i] >= y0 or lake_map[i] >= y0 then
|
||||
is_empty = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- If not, skip chunk
|
||||
if is_empty then
|
||||
local t = os.clock() - t0
|
||||
ngen = ngen + 1
|
||||
sumtime = sumtime + t
|
||||
sumtime2 = sumtime2 + t*t
|
||||
|
||||
minetest.log("verbose", "[mapgen_rivers] Skipping empty chunk (fully above ground level)")
|
||||
minetest.log("verbose", ("[mapgen_rivers] Done in %5.3f s"):format(t))
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local c_stone = minetest.get_content_id("mapgen_stone")
|
||||
local c_water = minetest.get_content_id("mapgen_water_source")
|
||||
local c_rwater = minetest.get_content_id("mapgen_river_water_source")
|
||||
|
||||
local c_dirt, c_lawn, c_dirtsnow, c_snow, c_sand, c_ice
|
||||
if use_biomes then
|
||||
c_dirt = minetest.get_content_id("default:dirt")
|
||||
c_lawn = minetest.get_content_id("default:dirt_with_grass")
|
||||
c_dirtsnow = minetest.get_content_id("default:dirt_with_snow")
|
||||
c_snow = minetest.get_content_id("default:snowblock")
|
||||
c_sand = minetest.get_content_id("default:sand")
|
||||
c_ice = minetest.get_content_id("default:ice")
|
||||
end
|
||||
|
||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
||||
vm:get_data(data)
|
||||
|
||||
local a = VoxelArea:new({MinEdge = emin, MaxEdge = emax})
|
||||
local ystride = a.ystride -- Tip : the ystride of a VoxelArea is the number to add to the array index to get the index of the position above. It's faster because it avoids to completely recalculate the index.
|
||||
|
||||
local nid = mapsize.x*(mapsize.y-1) + 1
|
||||
local incrY = -mapsize.x
|
||||
local incrX = 1 - mapsize.y*incrY
|
||||
local incrZ = mapsize.x*mapsize.y - mapsize.x*incrX - mapsize.x*mapsize.y*incrY
|
||||
|
||||
local i2d = 1
|
||||
|
||||
for z = minp.z, maxp.z do
|
||||
for x = minp.x, maxp.x do
|
||||
local ivm = a:index(x, maxp.y+1, z)
|
||||
local ground_above = false
|
||||
local temperature
|
||||
if use_biomes then
|
||||
temperature = noise_heat_map[i2d]+noise_heat_blend_map[i2d]
|
||||
end
|
||||
local terrain, lake
|
||||
if not use_distort then
|
||||
terrain = terrain_map[i2d]
|
||||
lake = lake_map[i2d]
|
||||
end
|
||||
|
||||
for y = maxp.y+1, minp.y, -1 do
|
||||
if use_distort then
|
||||
local xn = noise_x_map[nid]
|
||||
local zn = noise_z_map[nid]
|
||||
local x0 = floor(xn)
|
||||
local z0 = floor(zn)
|
||||
|
||||
local i0 = i_origin + z0*incr + x0
|
||||
local i1 = i0+1
|
||||
local i2 = i1+incr
|
||||
local i3 = i2-1
|
||||
|
||||
terrain = interp(terrain_map[i0], terrain_map[i1], terrain_map[i2], terrain_map[i3], xn-x0, zn-z0)
|
||||
lake = min(lake_map[i0], lake_map[i1], lake_map[i2], lake_map[i3])
|
||||
end
|
||||
|
||||
if y <= maxp.y then
|
||||
|
||||
local is_lake = lake > terrain
|
||||
if y <= terrain then
|
||||
if not use_biomes or y <= terrain-1 or ground_above then
|
||||
data[ivm] = c_stone
|
||||
elseif is_lake or y < sea_level then
|
||||
data[ivm] = c_sand
|
||||
else
|
||||
local temperature_y = temperature - y*elevation_chill
|
||||
if temperature_y >= 15 then
|
||||
data[ivm] = c_lawn
|
||||
elseif temperature_y >= 0 then
|
||||
data[ivm] = c_dirtsnow
|
||||
else
|
||||
data[ivm] = c_snow
|
||||
end
|
||||
end
|
||||
elseif y <= lake and lake > sea_level then
|
||||
if not use_biomes or temperature - y*elevation_chill >= 0 then
|
||||
data[ivm] = c_rwater
|
||||
else
|
||||
data[ivm] = c_ice
|
||||
end
|
||||
elseif y <= sea_level then
|
||||
data[ivm] = c_water
|
||||
end
|
||||
end
|
||||
|
||||
ground_above = y <= terrain
|
||||
|
||||
ivm = ivm - ystride
|
||||
if use_distort then
|
||||
nid = nid + incrY
|
||||
end
|
||||
end
|
||||
|
||||
if use_distort then
|
||||
nid = nid + incrX
|
||||
end
|
||||
i2d = i2d + 1
|
||||
end
|
||||
|
||||
if use_distort then
|
||||
nid = nid + incrZ
|
||||
end
|
||||
end
|
||||
|
||||
if use_biomegen_mod then
|
||||
biomegen.generate_all(data, a, vm, minp, maxp, seed)
|
||||
else
|
||||
vm:set_data(data)
|
||||
minetest.generate_ores(vm, minp, maxp)
|
||||
end
|
||||
|
||||
vm:set_lighting({day = 0, night = 0})
|
||||
vm:calc_lighting()
|
||||
vm:update_liquids()
|
||||
vm:write_to_map()
|
||||
|
||||
local t = os.clock()-t0
|
||||
ngen = ngen + 1
|
||||
sumtime = sumtime + t
|
||||
sumtime2 = sumtime2 + t*t
|
||||
minetest.log("verbose", ("[mapgen_rivers] Done in %5.3f s"):format(t))
|
||||
end
|
||||
|
||||
minetest.register_on_generated(generate)
|
||||
minetest.register_on_shutdown(function()
|
||||
local avg = sumtime / ngen
|
||||
local std = math.sqrt(sumtime2/ngen - avg*avg)
|
||||
minetest.log("action", ("[mapgen_rivers] Mapgen statistics:\n- Mapgen calls: %4d\n- Mean time: %5.3f s\n- Standard deviation: %5.3f s"):format(ngen, avg, std))
|
||||
end)
|
||||
if not mapgen_rivers.use_mapgen_thread then
|
||||
dofile(modpath .. '/mapgen.lua')
|
||||
end
|
||||
|
||||
-- Setup a metatable to load grid on request if not present
|
||||
local mt = {
|
||||
__index = function(_, field)
|
||||
if field == 'grid' then
|
||||
dofile(modpath .. '/load_grid.lua')
|
||||
return mapgen_rivers.grid
|
||||
end
|
||||
end,
|
||||
}
|
||||
setmetatable(mapgen_rivers, mt)
|
||||
|
98
load.lua
98
load.lua
@ -1,98 +0,0 @@
|
||||
local worldpath = mapgen_rivers.world_data_path
|
||||
|
||||
local floor = math.floor
|
||||
local sbyte, schar = string.byte, string.char
|
||||
local unpk = unpack
|
||||
|
||||
function mapgen_rivers.load_map(filename, bytes, signed, size, converter)
|
||||
local file = io.open(worldpath .. filename, 'rb')
|
||||
local data = file:read('*all')
|
||||
if #data < bytes*size then
|
||||
data = minetest.decompress(data)
|
||||
end
|
||||
|
||||
local map = {}
|
||||
|
||||
for i=1, size do
|
||||
local i0 = (i-1)*bytes+1
|
||||
local elements = {data:byte(i0, i1)}
|
||||
local n = sbyte(data, i0)
|
||||
if signed and n >= 128 then
|
||||
n = n - 256
|
||||
end
|
||||
|
||||
for j=1, bytes-1 do
|
||||
n = n*256 + sbyte(data, i0+j)
|
||||
end
|
||||
|
||||
map[i] = n
|
||||
end
|
||||
file:close()
|
||||
|
||||
if converter then
|
||||
for i=1, size do
|
||||
map[i] = converter(map[i])
|
||||
end
|
||||
end
|
||||
|
||||
return map
|
||||
end
|
||||
|
||||
local loader_mt = {
|
||||
__index = function(loader, i)
|
||||
local file = loader.file
|
||||
local bytes = loader.bytes
|
||||
file:seek('set', (i-1)*bytes)
|
||||
local strnum = file:read(bytes)
|
||||
|
||||
local n = sbyte(strnum, 1)
|
||||
if loader.signed and n >= 128 then
|
||||
n = n - 256
|
||||
end
|
||||
|
||||
for j=2, bytes do
|
||||
n = n*256 + sbyte(strnum, j)
|
||||
end
|
||||
|
||||
if loader.conv then
|
||||
n = loader.conv(n)
|
||||
end
|
||||
loader[i] = n
|
||||
return n
|
||||
end,
|
||||
}
|
||||
|
||||
function mapgen_rivers.interactive_loader(filename, bytes, signed, size, converter)
|
||||
local file = io.open(worldpath .. filename, 'rb')
|
||||
if file then
|
||||
minetest.register_on_shutdown(function()
|
||||
file:close()
|
||||
end)
|
||||
converter = converter or false
|
||||
return setmetatable({file=file, bytes=bytes, signed=signed, size=size, conv=converter}, loader_mt)
|
||||
end
|
||||
end
|
||||
|
||||
function mapgen_rivers.write_map(filename, data, bytes)
|
||||
local size = #data
|
||||
local file = io.open(worldpath .. filename, 'wb')
|
||||
|
||||
local bytelist = {}
|
||||
for j=1, bytes do
|
||||
bytelist[j] = 0
|
||||
end
|
||||
|
||||
for i=1, size do
|
||||
local n = floor(data[i])
|
||||
data[i] = n
|
||||
for j=bytes, 2, -1 do
|
||||
bytelist[j] = n % 256
|
||||
n = floor(n / 256)
|
||||
end
|
||||
bytelist[1] = n % 256
|
||||
|
||||
file:write(schar(unpk(bytelist)))
|
||||
end
|
||||
|
||||
file:close()
|
||||
end
|
107
load_grid.lua
Normal file
107
load_grid.lua
Normal file
@ -0,0 +1,107 @@
|
||||
local datapath = minetest.get_worldpath() .. "/river_data/"
|
||||
|
||||
local floor = math.floor
|
||||
local sbyte = string.byte
|
||||
local unpk = unpack
|
||||
|
||||
local load_map
|
||||
local use_interactive_loader
|
||||
if minetest.settings:has("mapgen_rivers_use_interactive_loader") then
|
||||
use_interactive_loader = minetest.settings:get_bool("mapgen_rivers_use_interactive_loader")
|
||||
else
|
||||
use_interactive_loader = not minetest.settings:get_bool("mapgen_rivers_load_all")
|
||||
end
|
||||
|
||||
if use_interactive_loader then
|
||||
local loader_mt = {
|
||||
__index = function(loader, i)
|
||||
local file = loader.file
|
||||
local bytes = loader.bytes
|
||||
file:seek('set', (i-1)*bytes)
|
||||
local strnum = file:read(bytes)
|
||||
|
||||
local n = sbyte(strnum, 1)
|
||||
if loader.signed and n >= 128 then
|
||||
n = n - 256
|
||||
end
|
||||
|
||||
for j=2, bytes do
|
||||
n = n*256 + sbyte(strnum, j)
|
||||
end
|
||||
|
||||
if loader.conv then
|
||||
n = loader.conv(n)
|
||||
end
|
||||
loader[i] = n
|
||||
return n
|
||||
end,
|
||||
}
|
||||
|
||||
load_map = function(filename, bytes, signed, size, converter)
|
||||
local file = io.open(datapath .. filename, 'rb')
|
||||
if file then
|
||||
minetest.register_on_shutdown(function()
|
||||
file:close()
|
||||
end)
|
||||
converter = converter or false
|
||||
return setmetatable({file=file, bytes=bytes, signed=signed, size=size, conv=converter}, loader_mt)
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
load_map = function(filename, bytes, signed, size, converter)
|
||||
local file = io.open(datapath .. filename, 'rb')
|
||||
local data = file:read('*all')
|
||||
if #data < bytes*size then
|
||||
data = minetest.decompress(data)
|
||||
end
|
||||
|
||||
local map = {}
|
||||
|
||||
for i=1, size do
|
||||
local i0 = (i-1)*bytes+1
|
||||
local i1 = i*bytes
|
||||
local elements = {data:byte(i0, i1)}
|
||||
local n = sbyte(data, i0)
|
||||
if signed and n >= 128 then
|
||||
n = n - 256
|
||||
end
|
||||
|
||||
for j=1, bytes-1 do
|
||||
n = n*256 + sbyte(data, i0+j)
|
||||
end
|
||||
|
||||
map[i] = n
|
||||
end
|
||||
file:close()
|
||||
|
||||
if converter then
|
||||
for i=1, size do
|
||||
map[i] = converter(map[i])
|
||||
end
|
||||
end
|
||||
|
||||
return map
|
||||
end
|
||||
end
|
||||
|
||||
local sfile = io.open(datapath..'size', 'r')
|
||||
assert(sfile)
|
||||
local X, Z = tonumber(sfile:read('*l')), tonumber(sfile:read('*l'))
|
||||
sfile:close()
|
||||
|
||||
local function offset_converter(o)
|
||||
return (o + 0.5) * (1/256)
|
||||
end
|
||||
|
||||
mapgen_rivers.grid = {
|
||||
size = {x=X, y=Z},
|
||||
dem = load_map('dem', 2, true, X*Z),
|
||||
lakes = load_map('lakes', 2, true, X*Z),
|
||||
dirs = load_map('dirs', 1, false, X*Z),
|
||||
rivers = load_map('rivers', 4, false, X*Z),
|
||||
|
||||
offset_x = load_map('offset_x', 1, true, X*Z, offset_converter),
|
||||
offset_y = load_map('offset_y', 1, true, X*Z, offset_converter);
|
||||
}
|
307
mapgen.lua
Normal file
307
mapgen.lua
Normal file
@ -0,0 +1,307 @@
|
||||
-- Recreate mod table if we are in a separate environment
|
||||
if not minetest.global_exists("mapgen_rivers") then
|
||||
mapgen_rivers = {use_mapgen_thread=true, thread='mapgen'}
|
||||
mapgen_rivers.settings = Settings(minetest.get_worldpath() .. '/mapgen_rivers.conf')
|
||||
end
|
||||
|
||||
if not mapgen_rivers.grid then
|
||||
dofile(minetest.get_modpath(minetest.get_current_modname()) .. '/load_grid.lua')
|
||||
end
|
||||
|
||||
local settings = mapgen_rivers.settings
|
||||
|
||||
local sea_level = tonumber(minetest.get_mapgen_setting("water_level"))
|
||||
local elevation_chill = tonumber(settings:get('elevation_chill'))
|
||||
local use_distort = settings:get_bool('distort')
|
||||
local use_biomes = settings:get_bool('biomes')
|
||||
local use_biomegen_mod = use_biomes and minetest.global_exists('biomegen')
|
||||
use_biomes = use_biomes and minetest.get_modpath("default") and not use_biomegen_mod
|
||||
|
||||
local noiseparams = {
|
||||
distort_x = settings:get_np_group('np_distort_x'),
|
||||
distort_z = settings:get_np_group('np_distort_z'),
|
||||
distort_amplitude = settings:get_np_group('np_distort_amplitude'),
|
||||
}
|
||||
|
||||
if use_biomes then
|
||||
noiseparams.heat = minetest.get_mapgen_setting_noiseparams("mg_biome_np_heat")
|
||||
noiseparams.heat.offset = noiseparams.heat.offset + sea_level / elevation_chill
|
||||
noiseparams.heat_blend = minetest.get_mapgen_setting_noiseparams("mg_biome_np_heat_blend")
|
||||
end
|
||||
|
||||
if use_biomegen_mod then
|
||||
biomegen.set_elevation_chill(elevation_chill)
|
||||
end
|
||||
|
||||
local heightmaps = dofile(minetest.get_modpath(minetest.get_current_modname()) .. '/heightmap.lua')
|
||||
|
||||
-- Linear interpolation
|
||||
local function interp(v00, v01, v11, v10, xf, zf)
|
||||
local v0 = v01*xf + v00*(1-xf)
|
||||
local v1 = v11*xf + v10*(1-xf)
|
||||
return v1*zf + v0*(1-zf)
|
||||
end
|
||||
|
||||
-- Localize for performance
|
||||
local floor, min = math.floor, math.min
|
||||
|
||||
local data = {}
|
||||
|
||||
local noise_x_obj, noise_z_obj, noise_distort_obj, noise_heat_obj, noise_heat_blend_obj
|
||||
local noise_x_map = {}
|
||||
local noise_z_map = {}
|
||||
local noise_distort_map = {}
|
||||
local noise_heat_map = {}
|
||||
local noise_heat_blend_map = {}
|
||||
local mapsize
|
||||
local init = false
|
||||
|
||||
local sumtime = 0
|
||||
local sumtime2 = 0
|
||||
local ngen = 0
|
||||
|
||||
local function init_mapgen(chulens)
|
||||
mapsize = {
|
||||
x = chulens.x,
|
||||
y = chulens.y+1,
|
||||
z = chulens.z,
|
||||
}
|
||||
if use_distort then
|
||||
noise_x_obj = minetest.get_perlin_map(noiseparams.distort_x, mapsize)
|
||||
noise_z_obj = minetest.get_perlin_map(noiseparams.distort_z, mapsize)
|
||||
noise_distort_obj = minetest.get_perlin_map(noiseparams.distort_amplitude, chulens)
|
||||
end
|
||||
if use_biomes then
|
||||
noise_heat_obj = minetest.get_perlin_map(noiseparams.heat, chulens)
|
||||
noise_heat_blend_obj = minetest.get_perlin_map(noiseparams.heat_blend, chulens)
|
||||
end
|
||||
end
|
||||
|
||||
local function generate(vm, minp, maxp, seed)
|
||||
minetest.log("info", ("[mapgen_rivers] Generating from %s to %s"):format(minetest.pos_to_string(minp), minetest.pos_to_string(maxp)))
|
||||
|
||||
local chulens = {
|
||||
x = maxp.x-minp.x+1,
|
||||
y = maxp.y-minp.y+1,
|
||||
z = maxp.z-minp.z+1,
|
||||
}
|
||||
|
||||
if not init then
|
||||
init_mapgen(chulens)
|
||||
init = true
|
||||
end
|
||||
|
||||
local t0 = os.clock()
|
||||
local minp2d = {x=minp.x, y=minp.z}
|
||||
if use_distort then
|
||||
noise_x_obj:get_3d_map_flat(minp, noise_x_map)
|
||||
noise_z_obj:get_3d_map_flat(minp, noise_z_map)
|
||||
noise_distort_obj:get_2d_map_flat(minp2d, noise_distort_map)
|
||||
end
|
||||
if use_biomes then
|
||||
noise_heat_obj:get_2d_map_flat(minp2d, noise_heat_map)
|
||||
noise_heat_blend_obj:get_2d_map_flat(minp2d, noise_heat_blend_map)
|
||||
end
|
||||
|
||||
local terrain_map, lake_map, incr, i_origin
|
||||
|
||||
if use_distort then
|
||||
local xmin, xmax, zmin, zmax = minp.x, maxp.x, minp.z, maxp.z
|
||||
local i = 0
|
||||
local i2d = 0
|
||||
for z=minp.z, maxp.z do
|
||||
for y=minp.y, maxp.y+1 do
|
||||
for x=minp.x, maxp.x do
|
||||
i = i+1
|
||||
i2d = i2d+1
|
||||
local distort = noise_distort_map[i2d]
|
||||
local xv = noise_x_map[i]*distort + x
|
||||
if xv < xmin then xmin = xv end
|
||||
if xv > xmax then xmax = xv end
|
||||
noise_x_map[i] = xv
|
||||
local zv = noise_z_map[i]*distort + z
|
||||
if zv < zmin then zmin = zv end
|
||||
if zv > zmax then zmax = zv end
|
||||
noise_z_map[i] = zv
|
||||
end
|
||||
i2d = i2d-chulens.x
|
||||
end
|
||||
end
|
||||
|
||||
local pminp = {x=floor(xmin), z=floor(zmin)}
|
||||
local pmaxp = {x=floor(xmax)+1, z=floor(zmax)+1}
|
||||
incr = pmaxp.x-pminp.x+1
|
||||
i_origin = 1 - pminp.z*incr - pminp.x
|
||||
terrain_map, lake_map = heightmaps(pminp, pmaxp)
|
||||
else
|
||||
terrain_map, lake_map = heightmaps(minp, maxp)
|
||||
end
|
||||
|
||||
-- Check that there is at least one position that reaches min y
|
||||
if minp.y > sea_level then
|
||||
local y0 = minp.y
|
||||
local is_empty = true
|
||||
for i=1, #terrain_map do
|
||||
if terrain_map[i] >= y0 or lake_map[i] >= y0 then
|
||||
is_empty = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- If not, skip chunk
|
||||
if is_empty then
|
||||
local t = os.clock() - t0
|
||||
ngen = ngen + 1
|
||||
sumtime = sumtime + t
|
||||
sumtime2 = sumtime2 + t*t
|
||||
|
||||
minetest.log("verbose", "[mapgen_rivers] Skipping empty chunk (fully above ground level)")
|
||||
minetest.log("verbose", ("[mapgen_rivers] Done in %5.3f s"):format(t))
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local c_stone = minetest.get_content_id("mapgen_stone")
|
||||
local c_water = minetest.get_content_id("mapgen_water_source")
|
||||
local c_rwater = minetest.get_content_id("mapgen_river_water_source")
|
||||
|
||||
local c_dirt, c_lawn, c_dirtsnow, c_snow, c_sand, c_ice
|
||||
if use_biomes then
|
||||
c_dirt = minetest.get_content_id("default:dirt")
|
||||
c_lawn = minetest.get_content_id("default:dirt_with_grass")
|
||||
c_dirtsnow = minetest.get_content_id("default:dirt_with_snow")
|
||||
c_snow = minetest.get_content_id("default:snowblock")
|
||||
c_sand = minetest.get_content_id("default:sand")
|
||||
c_ice = minetest.get_content_id("default:ice")
|
||||
end
|
||||
|
||||
local emin, emax = vm:get_emerged_area()
|
||||
vm:get_data(data)
|
||||
|
||||
local a = VoxelArea:new({MinEdge = emin, MaxEdge = emax})
|
||||
local ystride = a.ystride -- Tip : the ystride of a VoxelArea is the number to add to the array index to get the index of the position above. It's faster because it avoids to completely recalculate the index.
|
||||
|
||||
local nid = mapsize.x*(mapsize.y-1) + 1
|
||||
local incrY = -mapsize.x
|
||||
local incrX = 1 - mapsize.y*incrY
|
||||
local incrZ = mapsize.x*mapsize.y - mapsize.x*incrX - mapsize.x*mapsize.y*incrY
|
||||
|
||||
local i2d = 1
|
||||
|
||||
for z = minp.z, maxp.z do
|
||||
for x = minp.x, maxp.x do
|
||||
local ivm = a:index(x, maxp.y+1, z)
|
||||
local ground_above = false
|
||||
local temperature
|
||||
if use_biomes then
|
||||
temperature = noise_heat_map[i2d]+noise_heat_blend_map[i2d]
|
||||
end
|
||||
local terrain, lake
|
||||
if not use_distort then
|
||||
terrain = terrain_map[i2d]
|
||||
lake = lake_map[i2d]
|
||||
end
|
||||
|
||||
for y = maxp.y+1, minp.y, -1 do
|
||||
if use_distort then
|
||||
local xn = noise_x_map[nid]
|
||||
local zn = noise_z_map[nid]
|
||||
local x0 = floor(xn)
|
||||
local z0 = floor(zn)
|
||||
|
||||
local i0 = i_origin + z0*incr + x0
|
||||
local i1 = i0+1
|
||||
local i2 = i1+incr
|
||||
local i3 = i2-1
|
||||
|
||||
terrain = interp(terrain_map[i0], terrain_map[i1], terrain_map[i2], terrain_map[i3], xn-x0, zn-z0)
|
||||
lake = min(lake_map[i0], lake_map[i1], lake_map[i2], lake_map[i3])
|
||||
end
|
||||
|
||||
if y <= maxp.y then
|
||||
|
||||
local is_lake = lake > terrain
|
||||
if y <= terrain then
|
||||
if not use_biomes or y <= terrain-1 or ground_above then
|
||||
data[ivm] = c_stone
|
||||
elseif is_lake or y < sea_level then
|
||||
data[ivm] = c_sand
|
||||
else
|
||||
local temperature_y = temperature - y*elevation_chill
|
||||
if temperature_y >= 15 then
|
||||
data[ivm] = c_lawn
|
||||
elseif temperature_y >= 0 then
|
||||
data[ivm] = c_dirtsnow
|
||||
else
|
||||
data[ivm] = c_snow
|
||||
end
|
||||
end
|
||||
elseif y <= lake and lake > sea_level then
|
||||
if not use_biomes or temperature - y*elevation_chill >= 0 then
|
||||
data[ivm] = c_rwater
|
||||
else
|
||||
data[ivm] = c_ice
|
||||
end
|
||||
elseif y <= sea_level then
|
||||
data[ivm] = c_water
|
||||
end
|
||||
end
|
||||
|
||||
ground_above = y <= terrain
|
||||
|
||||
ivm = ivm - ystride
|
||||
if use_distort then
|
||||
nid = nid + incrY
|
||||
end
|
||||
end
|
||||
|
||||
if use_distort then
|
||||
nid = nid + incrX
|
||||
end
|
||||
i2d = i2d + 1
|
||||
end
|
||||
|
||||
if use_distort then
|
||||
nid = nid + incrZ
|
||||
end
|
||||
end
|
||||
|
||||
if use_biomegen_mod then
|
||||
biomegen.generate_all(data, a, vm, minp, maxp, seed)
|
||||
else
|
||||
vm:set_data(data)
|
||||
minetest.generate_ores(vm, minp, maxp)
|
||||
end
|
||||
|
||||
vm:set_lighting({day = 0, night = 0})
|
||||
vm:calc_lighting()
|
||||
vm:update_liquids()
|
||||
if mapgen_rivers.thread == "main" then
|
||||
vm:write_to_map()
|
||||
end
|
||||
|
||||
local t = os.clock()-t0
|
||||
ngen = ngen + 1
|
||||
sumtime = sumtime + t
|
||||
sumtime2 = sumtime2 + t*t
|
||||
minetest.log("verbose", ("[mapgen_rivers] Done in %5.3f s"):format(t))
|
||||
end
|
||||
|
||||
if mapgen_rivers.thread == "main" then
|
||||
minetest.register_on_generated(function(minp, maxp, seed)
|
||||
local vm = minetest.get_mapgen_object("voxelmanip")
|
||||
generate(vm, minp, maxp, seed)
|
||||
end)
|
||||
elseif mapgen_rivers.thread == "mapgen" then
|
||||
minetest.register_on_generated(generate)
|
||||
end
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
if ngen == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local avg = sumtime / ngen
|
||||
local std = math.sqrt(sumtime2/ngen - avg*avg)
|
||||
minetest.log("action", ("[mapgen_rivers] Mapgen statistics:\n- Mapgen calls: %4d\n- Mean time: %5.3f s\n- Standard deviation: %5.3f s"):format(ngen, avg, std))
|
||||
end)
|
80
noises.lua
80
noises.lua
@ -1,80 +0,0 @@
|
||||
local def_setting = mapgen_rivers.define_setting
|
||||
|
||||
mapgen_rivers.noise_params = {
|
||||
base = def_setting('np_base', 'noise', {
|
||||
offset = 0,
|
||||
scale = 300,
|
||||
seed = 2469,
|
||||
octaves = 8,
|
||||
spread = {x=2048, y=2048, z=2048},
|
||||
persist = 0.6,
|
||||
lacunarity = 2,
|
||||
flags = "eased",
|
||||
}),
|
||||
|
||||
distort_x = def_setting('np_distort_x', 'noise', {
|
||||
offset = 0,
|
||||
scale = 1,
|
||||
seed = -4574,
|
||||
spread = {x=64, y=32, z=64},
|
||||
octaves = 3,
|
||||
persistence = 0.75,
|
||||
lacunarity = 2,
|
||||
}),
|
||||
|
||||
distort_z = def_setting('np_distort_z', 'noise', {
|
||||
offset = 0,
|
||||
scale = 1,
|
||||
seed = -7940,
|
||||
spread = {x=64, y=32, z=64},
|
||||
octaves = 3,
|
||||
persistence = 0.75,
|
||||
lacunarity = 2,
|
||||
}),
|
||||
|
||||
distort_amplitude = def_setting('np_distort_amplitude', 'noise', {
|
||||
offset = 0,
|
||||
scale = 10,
|
||||
seed = 676,
|
||||
spread = {x=1024, y=1024, z=1024},
|
||||
octaves = 5,
|
||||
persistence = 0.5,
|
||||
lacunarity = 2,
|
||||
flags = "absvalue",
|
||||
}),
|
||||
|
||||
heat = minetest.get_mapgen_setting_noiseparams('mg_biome_np_heat'),
|
||||
heat_blend = minetest.get_mapgen_setting_noiseparams('mg_biome_np_heat_blend'),
|
||||
}
|
||||
|
||||
-- Convert to number because Minetest API is not able to do it cleanly...
|
||||
for name, np in pairs(mapgen_rivers.noise_params) do
|
||||
for field, value in pairs(np) do
|
||||
if field ~= 'flags' and type(value) == 'string' then
|
||||
np[field] = tonumber(value) or value
|
||||
elseif field == 'spread' then
|
||||
for dir, v in pairs(value) do
|
||||
value[dir] = tonumber(v) or v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local heat = mapgen_rivers.noise_params.heat
|
||||
local base = mapgen_rivers.noise_params.base
|
||||
local settings = mapgen_rivers.settings
|
||||
heat.offset = heat.offset + settings.sea_level * settings.elevation_chill
|
||||
base.spread.x = base.spread.x / settings.blocksize
|
||||
base.spread.y = base.spread.y / settings.blocksize
|
||||
base.spread.z = base.spread.z / settings.blocksize
|
||||
|
||||
for name, np in pairs(mapgen_rivers.noise_params) do
|
||||
local lac = np.lacunarity or 2
|
||||
if lac > 1 then
|
||||
local omax = math.floor(math.log(math.min(np.spread.x, np.spread.y, np.spread.z)) / math.log(lac))+1
|
||||
if np.octaves > omax then
|
||||
minetest.log("warning", "[mapgen_rivers] Noise " .. name .. ": 'octaves' reduced to " .. omax)
|
||||
np.octaves = omax
|
||||
end
|
||||
end
|
||||
end
|
104
polygons.lua
104
polygons.lua
@ -1,91 +1,17 @@
|
||||
local modpath = mapgen_rivers.modpath
|
||||
local mod_data_path = modpath .. 'river_data/'
|
||||
if not io.open(mod_data_path .. 'size', 'r') then
|
||||
mod_data_path = modpath .. 'demo_data/'
|
||||
end
|
||||
|
||||
local world_data_path = mapgen_rivers.world_data_path
|
||||
minetest.mkdir(world_data_path)
|
||||
|
||||
dofile(modpath .. 'load.lua')
|
||||
|
||||
mapgen_rivers.grid = {}
|
||||
|
||||
local X = mapgen_rivers.settings.grid_x_size
|
||||
local Z = mapgen_rivers.settings.grid_z_size
|
||||
|
||||
local function offset_converter(o)
|
||||
return (o + 0.5) * (1/256)
|
||||
end
|
||||
|
||||
local load_all = mapgen_rivers.settings.load_all
|
||||
|
||||
-- Try to read file 'size'
|
||||
local sfile = io.open(world_data_path..'size', 'r')
|
||||
local first_mapgen = true
|
||||
if sfile then
|
||||
X, Z = tonumber(sfile:read('*l')), tonumber(sfile:read('*l'))
|
||||
sfile:close()
|
||||
first_mapgen = false
|
||||
end
|
||||
|
||||
if first_mapgen then
|
||||
-- Generate a map!!
|
||||
local pregenerate = dofile(mapgen_rivers.modpath .. '/pregenerate.lua')
|
||||
minetest.register_on_mods_loaded(function()
|
||||
minetest.log("action", '[mapgen_rivers] Generating grid, this may take a while...')
|
||||
pregenerate(load_all)
|
||||
|
||||
if load_all then
|
||||
local offset_x = mapgen_rivers.grid.offset_x
|
||||
local offset_y = mapgen_rivers.grid.offset_y
|
||||
for i=1, X*Z do
|
||||
offset_x[i] = offset_converter(offset_x[i])
|
||||
offset_y[i] = offset_converter(offset_y[i])
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- if data not already loaded
|
||||
if not (first_mapgen and load_all) then
|
||||
local load_map
|
||||
if load_all then
|
||||
load_map = mapgen_rivers.load_map
|
||||
else
|
||||
load_map = mapgen_rivers.interactive_loader
|
||||
end
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
if load_all then
|
||||
minetest.log("action", '[mapgen_rivers] Loading full grid')
|
||||
else
|
||||
minetest.log("action", '[mapgen_rivers] Loading grid as interactive loaders')
|
||||
end
|
||||
local grid = mapgen_rivers.grid
|
||||
|
||||
grid.dem = load_map('dem', 2, true, X*Z)
|
||||
grid.lakes = load_map('lakes', 2, true, X*Z)
|
||||
grid.dirs = load_map('dirs', 1, false, X*Z)
|
||||
grid.rivers = load_map('rivers', 4, false, X*Z)
|
||||
|
||||
grid.offset_x = load_map('offset_x', 1, true, X*Z, offset_converter)
|
||||
grid.offset_y = load_map('offset_y', 1, true, X*Z, offset_converter)
|
||||
end)
|
||||
end
|
||||
|
||||
mapgen_rivers.grid.size = {x=X, y=Z}
|
||||
local X = mapgen_rivers.grid.size.x
|
||||
local Z = mapgen_rivers.grid.size.y
|
||||
|
||||
local function index(x, z)
|
||||
return z*X+x+1
|
||||
end
|
||||
|
||||
local blocksize = mapgen_rivers.settings.blocksize
|
||||
local min_catchment = mapgen_rivers.settings.min_catchment
|
||||
local max_catchment = mapgen_rivers.settings.max_catchment
|
||||
local settings = mapgen_rivers.settings
|
||||
|
||||
local blocksize = tonumber(settings:get('blocksize'))
|
||||
local min_catchment = tonumber(settings:get('min_catchment'))
|
||||
|
||||
local map_offset = {x=0, z=0}
|
||||
if mapgen_rivers.settings.center then
|
||||
if settings:get_bool('center') then
|
||||
map_offset.x = blocksize*X/2
|
||||
map_offset.z = blocksize*Z/2
|
||||
end
|
||||
@ -93,8 +19,8 @@ end
|
||||
-- Localize for performance
|
||||
local floor, ceil, min, max, abs = math.floor, math.ceil, math.min, math.max, math.abs
|
||||
|
||||
local min_catchment = mapgen_rivers.settings.min_catchment / (blocksize*blocksize)
|
||||
local wpower = mapgen_rivers.settings.river_widening_power
|
||||
min_catchment = min_catchment / (blocksize*blocksize)
|
||||
local wpower = settings:get('river_widening_power')
|
||||
local wfactor = 1/(2*blocksize * min_catchment^wpower)
|
||||
local function river_width(flow)
|
||||
flow = abs(flow)
|
||||
@ -106,14 +32,14 @@ local function river_width(flow)
|
||||
end
|
||||
|
||||
local noise_heat -- Need a large-scale noise here so no heat blend
|
||||
local elevation_chill = mapgen_rivers.settings.elevation_chill
|
||||
local elevation_chill = settings:get_bool('elevation_chill')
|
||||
local function get_temperature(x, y, z)
|
||||
local pos = {x=x, y=z}
|
||||
return noise_heat:get2d(pos) - y*elevation_chill
|
||||
end
|
||||
|
||||
local glaciers = mapgen_rivers.settings.glaciers
|
||||
local glacier_factor = mapgen_rivers.settings.glacier_factor
|
||||
local glaciers = settings:get_bool('glaciers')
|
||||
local glacier_factor = tonumber(settings:get('glacier_factor'))
|
||||
|
||||
local init = false
|
||||
|
||||
@ -236,15 +162,15 @@ local function make_polygons(minp, maxp)
|
||||
end
|
||||
end
|
||||
|
||||
polygon.river_corners = {riverA, riverB, riverC, riverD}
|
||||
polygon.river_corners = {riverA, 1-riverB, 2-riverC, 1-riverD}
|
||||
|
||||
-- Flow directions
|
||||
local dirA, dirB, dirC, dirD = dirs[iA], dirs[iB], dirs[iC], dirs[iD]
|
||||
-- Determine the river flux on the edges, by testing dirs values
|
||||
local river_west = (dirA==1 and riverA or 0) + (dirD==3 and riverD or 0)
|
||||
local river_north = (dirA==2 and riverA or 0) + (dirB==4 and riverB or 0)
|
||||
local river_east = (dirB==1 and riverB or 0) + (dirC==3 and riverC or 0)
|
||||
local river_south = (dirD==2 and riverD or 0) + (dirC==4 and riverC or 0)
|
||||
local river_east = 1 - (dirB==1 and riverB or 0) - (dirC==3 and riverC or 0)
|
||||
local river_south = 1 - (dirD==2 and riverD or 0) - (dirC==4 and riverC or 0)
|
||||
|
||||
polygon.rivers = {river_west, river_north, river_east, river_south}
|
||||
end
|
||||
|
172
pregenerate.lua
172
pregenerate.lua
@ -1,81 +1,131 @@
|
||||
local EvolutionModel = dofile(mapgen_rivers.modpath .. '/terrainlib_lua/erosion.lua')
|
||||
local twist = dofile(mapgen_rivers.modpath .. '/terrainlib_lua/twist.lua')
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
|
||||
local blocksize = mapgen_rivers.settings.blocksize
|
||||
local tectonic_speed = mapgen_rivers.settings.tectonic_speed
|
||||
local EvolutionModel = dofile(modpath .. '/terrainlib_lua/erosion.lua')
|
||||
local twist = dofile(modpath .. '/terrainlib_lua/twist.lua')
|
||||
|
||||
local np_base = table.copy(mapgen_rivers.noise_params.base)
|
||||
local blocksize = tonumber(mapgen_rivers.settings:get("blocksize"))
|
||||
local tectonic_speed = tonumber(mapgen_rivers.settings:get("tectonic_speed"))
|
||||
|
||||
local evol_params = mapgen_rivers.settings.evol_params
|
||||
local np_base = mapgen_rivers.settings:get_np_group("np_base")
|
||||
np_base.spread.x = np_base.spread.x / blocksize
|
||||
np_base.spread.y = np_base.spread.y / blocksize
|
||||
np_base.spread.z = np_base.spread.z / blocksize
|
||||
|
||||
local time = mapgen_rivers.settings.evol_time
|
||||
local time_step = mapgen_rivers.settings.evol_time_step
|
||||
local evol_params = {
|
||||
K = tonumber(mapgen_rivers.settings:get("river_erosion_coef")),
|
||||
m = tonumber(mapgen_rivers.settings:get("river_erosion_power")),
|
||||
d = tonumber(mapgen_rivers.settings:get("difusive_erosion")),
|
||||
}
|
||||
|
||||
local time = tonumber(mapgen_rivers.settings:get("evol_time"))
|
||||
local time_step = tonumber(mapgen_rivers.settings:get("evol_time_step"))
|
||||
local niter = math.ceil(time/time_step)
|
||||
time_step = time / niter
|
||||
|
||||
local function pregenerate(keep_loaded)
|
||||
local grid = mapgen_rivers.grid
|
||||
local size = grid.size
|
||||
-- Setup the model
|
||||
local size = {
|
||||
x = tonumber(mapgen_rivers.settings:get("grid_x_size")),
|
||||
y = tonumber(mapgen_rivers.settings:get("grid_z_size")),
|
||||
}
|
||||
|
||||
local seed = tonumber(minetest.get_mapgen_setting("seed"))
|
||||
np_base.seed = (np_base.seed or 0) + seed
|
||||
local seed = tonumber(minetest.get_mapgen_setting("seed"))
|
||||
np_base.seed = (np_base.seed or 0) + seed
|
||||
|
||||
local nobj_base = PerlinNoiseMap(np_base, {x=size.x, y=1, z=size.y})
|
||||
local nobj_base = PerlinNoiseMap(np_base, {x=size.x, y=1, z=size.y})
|
||||
|
||||
local dem = nobj_base:get_3d_map_flat({x=0, y=0, z=0})
|
||||
dem.X = size.x
|
||||
dem.Y = size.y
|
||||
local dem = nobj_base:get_3d_map_flat({x=0, y=0, z=0})
|
||||
dem.X = size.x
|
||||
dem.Y = size.y
|
||||
|
||||
local model = EvolutionModel(evol_params)
|
||||
model.dem = dem
|
||||
local ref_dem = model:define_isostasy(dem)
|
||||
local model = EvolutionModel(evol_params)
|
||||
model.dem = dem
|
||||
local ref_dem = model:define_isostasy(dem)
|
||||
|
||||
local tectonic_step = tectonic_speed * time_step
|
||||
collectgarbage()
|
||||
for i=1, niter do
|
||||
minetest.log("info", "[mapgen_rivers] Iteration " .. i .. " of " .. niter)
|
||||
local tectonic_step = tectonic_speed * time_step
|
||||
collectgarbage()
|
||||
|
||||
model:diffuse(time_step)
|
||||
model:flow()
|
||||
model:erode(time_step)
|
||||
if i < niter then
|
||||
if tectonic_step ~= 0 then
|
||||
nobj_base:get_3d_map_flat({x=0, y=tectonic_step*i, z=0}, ref_dem)
|
||||
end
|
||||
model:isostasy()
|
||||
end
|
||||
-- Run the model
|
||||
for i=1, niter do
|
||||
minetest.log("info", "[mapgen_rivers] Iteration " .. i .. " of " .. niter)
|
||||
|
||||
collectgarbage()
|
||||
end
|
||||
model:diffuse(time_step)
|
||||
model:flow()
|
||||
|
||||
local mfloor = math.floor
|
||||
local mmin, mmax = math.min, math.max
|
||||
local offset_x, offset_y = twist(model.dirs, model.rivers, 5)
|
||||
for i=1, size.x*size.y do
|
||||
offset_x[i] = mmin(mmax(offset_x[i]*256, -128), 127)
|
||||
offset_y[i] = mmin(mmax(offset_y[i]*256, -128), 127)
|
||||
model:erode(time_step)
|
||||
if i < niter then
|
||||
if tectonic_step ~= 0 then
|
||||
nobj_base:get_3d_map_flat({x=0, y=tectonic_step*i, z=0}, ref_dem)
|
||||
end
|
||||
model:isostasy()
|
||||
end
|
||||
|
||||
mapgen_rivers.write_map('dem', model.dem, 2)
|
||||
mapgen_rivers.write_map('lakes', model.lakes, 2)
|
||||
mapgen_rivers.write_map('dirs', model.dirs, 1)
|
||||
mapgen_rivers.write_map('rivers', model.rivers, 4)
|
||||
mapgen_rivers.write_map('offset_x', offset_x, 1)
|
||||
mapgen_rivers.write_map('offset_y', offset_y, 1)
|
||||
local sfile = io.open(mapgen_rivers.world_data_path .. 'size', "w")
|
||||
sfile:write(size.x..'\n'..size.y)
|
||||
sfile:close()
|
||||
|
||||
if keep_loaded then
|
||||
grid.dem = model.dem
|
||||
grid.lakes = model.lakes
|
||||
grid.dirs = model.dirs
|
||||
grid.rivers = model.rivers
|
||||
grid.offset_x = offset_x
|
||||
grid.offset_y = offset_y
|
||||
end
|
||||
collectgarbage()
|
||||
end
|
||||
model:flow()
|
||||
|
||||
return pregenerate
|
||||
local mfloor = math.floor
|
||||
local mmin, mmax = math.min, math.max
|
||||
local offset_x, offset_y = twist(model.dirs, model.rivers, 5)
|
||||
for i=1, size.x*size.y do
|
||||
offset_x[i] = mmin(mmax(offset_x[i]*256, -128), 127)
|
||||
offset_y[i] = mmin(mmax(offset_y[i]*256, -128), 127)
|
||||
end
|
||||
|
||||
-- Write the results in the world directory
|
||||
local datapath = minetest.get_worldpath() .. "/river_data/"
|
||||
minetest.mkdir(datapath)
|
||||
|
||||
local function write_map(filename, data, bytes)
|
||||
local size = #data
|
||||
local file = io.open(datapath .. filename, 'wb')
|
||||
|
||||
local bytelist = {}
|
||||
for j=1, bytes do
|
||||
bytelist[j] = 0
|
||||
end
|
||||
|
||||
local unpk = unpack
|
||||
local schar = string.char
|
||||
local floor = math.floor
|
||||
for i=1, size do
|
||||
local n = floor(data[i])
|
||||
data[i] = n
|
||||
for j=bytes, 2, -1 do
|
||||
bytelist[j] = n % 256
|
||||
n = floor(n / 256)
|
||||
end
|
||||
bytelist[1] = n % 256
|
||||
|
||||
file:write(schar(unpk(bytelist)))
|
||||
end
|
||||
|
||||
file:close()
|
||||
end
|
||||
|
||||
write_map('dem', model.dem, 2)
|
||||
write_map('lakes', model.lakes, 2)
|
||||
write_map('dirs', model.dirs, 1)
|
||||
write_map('rivers', model.rivers, 4)
|
||||
write_map('offset_x', offset_x, 1)
|
||||
write_map('offset_y', offset_y, 1)
|
||||
local sfile = io.open(datapath .. 'size', "w")
|
||||
sfile:write(size.x..'\n'..size.y)
|
||||
sfile:close()
|
||||
|
||||
local use_interactive_loader
|
||||
if minetest.settings:has("mapgen_rivers_use_interactive_loader") then
|
||||
use_interactive_loader = minetest.settings:get_bool("mapgen_rivers_use_interactive_loader")
|
||||
else
|
||||
use_interactive_loader = not minetest.settings:get_bool("mapgen_rivers_load_all")
|
||||
end
|
||||
|
||||
if not use_interactive_loader then
|
||||
mapgen_rivers.grid = {
|
||||
size = size,
|
||||
dem = model.dem,
|
||||
lakes = model.lakes,
|
||||
dirs = model.dirs,
|
||||
rivers = model.rivers,
|
||||
offset_x = offset_x,
|
||||
offset_y = offset_y,
|
||||
}
|
||||
end
|
||||
|
148
settings.lua
148
settings.lua
@ -1,10 +1,11 @@
|
||||
local mtsettings = minetest.settings
|
||||
local mgrsettings = Settings(minetest.get_worldpath() .. '/mapgen_rivers.conf')
|
||||
local settings = Settings(minetest.get_worldpath() .. '/mapgen_rivers.conf')
|
||||
|
||||
mapgen_rivers.version = "1.0.2"
|
||||
mapgen_rivers.settings = settings
|
||||
|
||||
local previous_version_mt = mtsettings:get("mapgen_rivers_version") or "0.0"
|
||||
local previous_version_mgr = mgrsettings:get("version") or "0.0"
|
||||
local previous_version_mgr = settings:get("version") or "0.0"
|
||||
|
||||
if mapgen_rivers.version ~= previous_version_mt or mapgen_rivers.version ~= previous_version_mgr then
|
||||
local compat_mt, compat_mgr = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/compatibility.lua")
|
||||
@ -12,84 +13,103 @@ if mapgen_rivers.version ~= previous_version_mt or mapgen_rivers.version ~= prev
|
||||
compat_mt(mtsettings)
|
||||
end
|
||||
if mapgen_rivers.version ~= previous_version_mgr then
|
||||
compat_mgr(mgrsettings)
|
||||
compat_mgr(settings)
|
||||
end
|
||||
end
|
||||
|
||||
mtsettings:set("mapgen_rivers_version", mapgen_rivers.version)
|
||||
mgrsettings:set("version", mapgen_rivers.version)
|
||||
settings:set("version", mapgen_rivers.version)
|
||||
|
||||
function mapgen_rivers.define_setting(name, dtype, default)
|
||||
local modified = false
|
||||
local function verify_setting(name, dtype, default)
|
||||
if settings:has(name) then
|
||||
return
|
||||
end
|
||||
|
||||
modified = true
|
||||
local v = default
|
||||
local mtname = 'mapgen_rivers_' .. name
|
||||
local mthas = mtsettings:has(mtname)
|
||||
if dtype == "number" or dtype == "string" then
|
||||
local v = mgrsettings:get(name)
|
||||
if v == nil then
|
||||
v = mtsettings:get('mapgen_rivers_' .. name)
|
||||
if v == nil then
|
||||
if mthas then
|
||||
v = mtsettings:get(mtname)
|
||||
if dtype == "number" and tonumber(v) == nil then
|
||||
v = default
|
||||
end
|
||||
mgrsettings:set(name, v)
|
||||
end
|
||||
if dtype == "number" then
|
||||
return tonumber(v)
|
||||
else
|
||||
return v
|
||||
end
|
||||
settings:set(name, v)
|
||||
elseif dtype == "bool" then
|
||||
local v = mgrsettings:get_bool(name)
|
||||
if v == nil then
|
||||
v = mtsettings:get_bool('mapgen_rivers_' .. name)
|
||||
if v == nil then
|
||||
v = default
|
||||
end
|
||||
mgrsettings:set_bool(name, v)
|
||||
if mthas then
|
||||
v = mtsettings:get(mtname)
|
||||
end
|
||||
return v
|
||||
settings:set_bool(name, v)
|
||||
elseif dtype == "noise" then
|
||||
local v = mgrsettings:get_np_group(name)
|
||||
if v == nil then
|
||||
v = mtsettings:get_np_group('mapgen_rivers_' .. name)
|
||||
if v == nil then
|
||||
v = default
|
||||
end
|
||||
mgrsettings:set_np_group(name, v)
|
||||
if mthas then
|
||||
v = mtsettings:get_np_group(mtname)
|
||||
end
|
||||
return v
|
||||
settings:set_np_group(name, v)
|
||||
end
|
||||
end
|
||||
|
||||
local def_setting = mapgen_rivers.define_setting
|
||||
verify_setting('center', 'bool', true)
|
||||
verify_setting('blocksize', 'number', 15)
|
||||
verify_setting('min_catchment', 'number', 3600)
|
||||
verify_setting('river_widening_power', 'number', 0.5)
|
||||
verify_setting('riverbed_slope', 'number', 0.4)
|
||||
verify_setting('distort', 'bool', true)
|
||||
verify_setting('biomes', 'bool', true)
|
||||
verify_setting('glaciers', 'bool', false)
|
||||
verify_setting('glacier_factor', 'number', 8)
|
||||
verify_setting('elevation_chill', 'number', 0.25)
|
||||
verify_setting('grid_x_size', 'number', 1000)
|
||||
verify_setting('grid_z_size', 'number', 1000)
|
||||
verify_setting('river_erosion_coef', 'number', 0.5)
|
||||
verify_setting('river_erosion_power', 'number', 0.4)
|
||||
verify_setting('diffusive_erosion', 'number', 0.5)
|
||||
verify_setting('compensation_radius', 'number', 50)
|
||||
verify_setting('tectonic_speed', 'number', 70)
|
||||
verify_setting('evol_time', 'number', 10)
|
||||
verify_setting('evol_time_step', 'number', 1)
|
||||
|
||||
mapgen_rivers.settings = {
|
||||
center = def_setting('center', 'bool', true),
|
||||
blocksize = def_setting('blocksize', 'number', 15),
|
||||
sea_level = tonumber(minetest.get_mapgen_setting('water_level')),
|
||||
min_catchment = def_setting('min_catchment', 'number', 3600),
|
||||
river_widening_power = def_setting('river_widening_power', 'number', 0.5),
|
||||
riverbed_slope = def_setting('riverbed_slope', 'number', 0.4),
|
||||
distort = def_setting('distort', 'bool', true),
|
||||
biomes = def_setting('biomes', 'bool', true),
|
||||
glaciers = def_setting('glaciers', 'bool', false),
|
||||
glacier_factor = def_setting('glacier_factor', 'number', 8),
|
||||
elevation_chill = def_setting('elevation_chill', 'number', 0.25),
|
||||
verify_setting('np_base', 'noise', {
|
||||
offset = 0,
|
||||
scale = 300,
|
||||
seed = 2469,
|
||||
octaves = 8,
|
||||
spread = {x=2048, y=2048, z=2048},
|
||||
persist = 0.6,
|
||||
lacunarity = 2,
|
||||
flags = "eased",
|
||||
})
|
||||
verify_setting('np_distort_x', 'noise', {
|
||||
offset = 0,
|
||||
scale = 1,
|
||||
seed = -4574,
|
||||
spread = {x=64, y=32, z=64},
|
||||
octaves = 3,
|
||||
persistence = 0.75,
|
||||
lacunarity = 2,
|
||||
})
|
||||
verify_setting('np_distort_z', 'noise', {
|
||||
offset = 0,
|
||||
scale = 1,
|
||||
seed = -7940,
|
||||
spread = {x=64, y=32, z=64},
|
||||
octaves = 3,
|
||||
persistence = 0.75,
|
||||
lacunarity = 2,
|
||||
})
|
||||
verify_setting('np_distort_amplitude', 'noise', {
|
||||
offset = 0,
|
||||
scale = 10,
|
||||
seed = 676,
|
||||
spread = {x=1024, y=1024, z=1024},
|
||||
octaves = 5,
|
||||
persistence = 0.5,
|
||||
lacunarity = 2,
|
||||
flags = "absvalue",
|
||||
})
|
||||
|
||||
grid_x_size = def_setting('grid_x_size', 'number', 1000),
|
||||
grid_z_size = def_setting('grid_z_size', 'number', 1000),
|
||||
evol_params = {
|
||||
K = def_setting('river_erosion_coef', 'number', 0.5),
|
||||
m = def_setting('river_erosion_power', 'number', 0.4),
|
||||
d = def_setting('diffusive_erosion', 'number', 0.5),
|
||||
compensation_radius = def_setting('compensation_radius', 'number', 50),
|
||||
},
|
||||
tectonic_speed = def_setting('tectonic_speed', 'number', 70),
|
||||
evol_time = def_setting('evol_time', 'number', 10),
|
||||
evol_time_step = def_setting('evol_time_step', 'number', 1),
|
||||
|
||||
load_all = mtsettings:get_bool('mapgen_rivers_load_all')
|
||||
}
|
||||
|
||||
local function write_settings()
|
||||
mgrsettings:write()
|
||||
if modified then
|
||||
settings:write()
|
||||
end
|
||||
|
||||
minetest.register_on_mods_loaded(write_settings)
|
||||
minetest.register_on_shutdown(write_settings)
|
||||
|
@ -123,11 +123,7 @@ 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
|
||||
modpath = minetest.get_modpath(minetest.get_current_modname()) .. "/terrainlib_lua/"
|
||||
end
|
||||
|
||||
local rivermapper = dofile(modpath .. "rivermapper.lua")
|
||||
|
Reference in New Issue
Block a user