mirror of
https://gitlab.com/gaelysam/mapgen_rivers.git
synced 2024-12-29 12:20:41 +01:00
Refactor grid loading and reorganize code
Move grid management functions out of polygons.lua, in a new file called gridmanager.lua Explicitly call other files and grid management functions from init.lua to make workflow more apparent Move mapgen loop out of init.lua, into mapgen.lua
This commit is contained in:
parent
cd2a77803f
commit
6564d40b85
@ -4,7 +4,8 @@ local floor = math.floor
|
|||||||
local sbyte, schar = string.byte, string.char
|
local sbyte, schar = string.byte, string.char
|
||||||
local unpk = unpack
|
local unpk = unpack
|
||||||
|
|
||||||
function mapgen_rivers.load_map(filename, bytes, signed, size, converter)
|
-- Loading files
|
||||||
|
local function load_full_map(filename, bytes, signed, size, converter)
|
||||||
local file = io.open(worldpath .. filename, 'rb')
|
local file = io.open(worldpath .. filename, 'rb')
|
||||||
local data = file:read('*all')
|
local data = file:read('*all')
|
||||||
if #data < bytes*size then
|
if #data < bytes*size then
|
||||||
@ -62,18 +63,31 @@ local loader_mt = {
|
|||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapgen_rivers.interactive_loader(filename, bytes, signed, size, converter)
|
local function interactive_loader(filename, bytes, signed, size, converter)
|
||||||
local file = io.open(worldpath .. filename, 'rb')
|
local file = io.open(worldpath .. filename, 'rb')
|
||||||
if file then
|
if file then
|
||||||
minetest.register_on_shutdown(function()
|
|
||||||
file:close()
|
|
||||||
end)
|
|
||||||
converter = converter or false
|
converter = converter or false
|
||||||
return setmetatable({file=file, bytes=bytes, signed=signed, size=size, conv=converter}, loader_mt)
|
return setmetatable({file=file, bytes=bytes, signed=signed, size=size, conv=converter}, loader_mt)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mapgen_rivers.write_map(filename, data, bytes)
|
local load_methods = {
|
||||||
|
full = load_full_map,
|
||||||
|
interactive = interactive_loader,
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapgen_rivers.load_file(...)
|
||||||
|
local load_method = mapgen_rivers.settings.load_method
|
||||||
|
local load_func = load_methods[load_method]
|
||||||
|
if load_func then
|
||||||
|
return load_func(...)
|
||||||
|
else
|
||||||
|
minetest.log("error", ("[mapgen_rivers] Unknown load method %s"):format(load_method))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Writing files
|
||||||
|
function mapgen_rivers.write_file(filename, data, bytes)
|
||||||
local size = #data
|
local size = #data
|
||||||
local file = io.open(worldpath .. filename, 'wb')
|
local file = io.open(worldpath .. filename, 'wb')
|
||||||
|
|
146
gridmanager.lua
Normal file
146
gridmanager.lua
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
local world_data = mapgen_rivers.world_data_path
|
||||||
|
|
||||||
|
local registered_on_grid_loaded = {}
|
||||||
|
function mapgen_rivers.register_on_grid_loaded(func)
|
||||||
|
if type(func) == "function" then
|
||||||
|
registered_on_grid_loaded[#registered_on_grid_loaded+1] = func
|
||||||
|
else
|
||||||
|
minetest.log("error", "[mapgen_rivers] register_on_grid_loaded can only register functions!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function on_grid_loaded_callback(grid)
|
||||||
|
for _, func in ipairs(registered_on_grid_loaded) do
|
||||||
|
func(grid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function offset_conv(o)
|
||||||
|
return (o + 0.5) * (1/256)
|
||||||
|
end
|
||||||
|
|
||||||
|
local grid_maps_list = {
|
||||||
|
dem = {bytes=2, signed=true},
|
||||||
|
lakes = {bytes=2, signed=true},
|
||||||
|
dirs = {bytes=1, signed=false},
|
||||||
|
rivers = {bytes=4, signed=false},
|
||||||
|
|
||||||
|
offset_x = {bytes=1, signed=true, conv=offset_conv},
|
||||||
|
offset_y = {bytes=1, signed=true, conv=offset_conv},
|
||||||
|
}
|
||||||
|
|
||||||
|
local function apply_grid_conversion(grid)
|
||||||
|
if grid.load_method ~= "full" then
|
||||||
|
minetest.log("warning", ("Could not apply data conversion for load method %s"):format(grid.load_method))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if grid.conv_applied then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local size = grid.size.x * grid.size.y
|
||||||
|
for mapname, params in pairs(grid_maps_list) do
|
||||||
|
local conv = params.conv
|
||||||
|
if conv then
|
||||||
|
local map = grid[mapname]
|
||||||
|
for i=1, size do
|
||||||
|
map[i] = conv(map[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
grid.conv_applied = true
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mapgen_rivers.try_load_grid(grid)
|
||||||
|
local load_method = mapgen_rivers.settings.load_method
|
||||||
|
|
||||||
|
-- First, check whether a grid is already loaded with the appropriate method
|
||||||
|
if mapgen_rivers.grid and mapgen_rivers.grid.load_method == load_method then
|
||||||
|
if not mapgen_rivers.grid.conv_applied then
|
||||||
|
apply_grid_conversion(mapgen_rivers.grid)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
-- Then, check the provided argument is a valid grid
|
||||||
|
elseif grid and grid.load_method == load_method then
|
||||||
|
if not mapgen_rivers.grid.conv_applied then
|
||||||
|
apply_grid_conversion(grid)
|
||||||
|
end
|
||||||
|
mapgen_rivers.grid = grid
|
||||||
|
on_grid_loaded_callback(grid)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fall back to loading the grid from the files
|
||||||
|
local sfile = io.open(world_data .. 'size', 'r')
|
||||||
|
if not sfile then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local x, z = sfile:read('*n'), sfile:read('*n')
|
||||||
|
if not x or not z then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if load_method == "full" then
|
||||||
|
minetest.log("action", '[mapgen_rivers] Loading full grid')
|
||||||
|
elseif load_method == "interactive" then
|
||||||
|
minetest.log("action", '[mapgen_rivers] Loading grid as interactive loaders')
|
||||||
|
end
|
||||||
|
|
||||||
|
grid = {
|
||||||
|
load_method = load_method,
|
||||||
|
size = {x=x, y=z},
|
||||||
|
}
|
||||||
|
|
||||||
|
for map, params in pairs(grid_maps_list) do
|
||||||
|
grid[map] = mapgen_rivers.load_file(map, params.bytes, params.signed, x*z, params.conv)
|
||||||
|
end
|
||||||
|
grid.conv_applied = true
|
||||||
|
|
||||||
|
mapgen_rivers.grid = grid
|
||||||
|
on_grid_loaded_callback(grid)
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mapgen_rivers.generate_grid()
|
||||||
|
minetest.log("action", '[mapgen_rivers] Generating grid, this may take a while...')
|
||||||
|
local grid = {}
|
||||||
|
|
||||||
|
local blocksize = mapgen_rivers.settings.blocksize
|
||||||
|
local xsize = math.floor(mapgen_rivers.settings.map_x_size / blocksize)
|
||||||
|
local zsize = math.floor(mapgen_rivers.settings.map_z_size / blocksize)
|
||||||
|
grid.size = {x=xsize, y=zsize}
|
||||||
|
grid.conv_applied = false
|
||||||
|
|
||||||
|
if not mapgen_rivers.pregenerate then
|
||||||
|
minetest.log("error", "[mapgen_rivers] Pre-generation function is not available.")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
mapgen_rivers.pregenerate(grid)
|
||||||
|
|
||||||
|
return grid
|
||||||
|
end
|
||||||
|
|
||||||
|
function mapgen_rivers.write_grid(grid)
|
||||||
|
minetest.mkdir(world_data)
|
||||||
|
|
||||||
|
if grid.conv_applied then
|
||||||
|
minetest.log("error", '[mapgen_rivers] Could not write grid if data conversion is already done')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
for map, params in pairs(grid_maps_list) do
|
||||||
|
mapgen_rivers.write_file(map, grid[map], params.bytes)
|
||||||
|
end
|
||||||
|
|
||||||
|
local sfile = io.open(world_data .. 'size', "w")
|
||||||
|
sfile:write(grid.size.x..'\n'..grid.size.y)
|
||||||
|
sfile:close()
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
274
init.lua
274
init.lua
@ -4,275 +4,21 @@ local modpath = minetest.get_modpath(minetest.get_current_modname()) .. '/'
|
|||||||
mapgen_rivers.modpath = modpath
|
mapgen_rivers.modpath = modpath
|
||||||
mapgen_rivers.world_data_path = minetest.get_worldpath() .. '/river_data/'
|
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')
|
dofile(modpath .. 'settings.lua')
|
||||||
|
dofile(modpath .. 'gridmanager.lua')
|
||||||
|
dofile(modpath .. 'gridio.lua')
|
||||||
dofile(modpath .. 'polygons.lua')
|
dofile(modpath .. 'polygons.lua')
|
||||||
dofile(modpath .. 'heightmap.lua')
|
dofile(modpath .. 'heightmap.lua')
|
||||||
|
dofile(modpath .. 'mapgen.lua')
|
||||||
|
|
||||||
local sea_level = mapgen_rivers.settings.sea_level
|
minetest.register_on_mods_loaded(function()
|
||||||
local elevation_chill = mapgen_rivers.settings.elevation_chill
|
local exist = mapgen_rivers.try_load_grid()
|
||||||
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
|
if not exist then -- If grid does not exist yet, generate it
|
||||||
biomegen.set_elevation_chill(elevation_chill)
|
dofile(modpath .. 'pregenerate.lua')
|
||||||
end
|
|
||||||
|
|
||||||
-- Linear interpolation
|
local grid = mapgen_rivers.generate_grid()
|
||||||
local function interp(v00, v01, v11, v10, xf, zf)
|
mapgen_rivers.write_grid(grid)
|
||||||
local v0 = v01*xf + v00*(1-xf)
|
mapgen_rivers.try_load_grid(grid) -- Reload if needed
|
||||||
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 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
|
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 = mapgen_rivers.make_heightmaps(pminp, pmaxp)
|
|
||||||
else
|
|
||||||
terrain_map, lake_map = mapgen_rivers.make_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 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)
|
end)
|
||||||
|
269
mapgen.lua
Normal file
269
mapgen.lua
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
function mapgen_rivers.make_chunk(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 = mapgen_rivers.make_heightmaps(pminp, pmaxp)
|
||||||
|
else
|
||||||
|
terrain_map, lake_map = mapgen_rivers.make_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 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(mapgen_rivers.make_chunk)
|
||||||
|
|
||||||
|
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)
|
90
polygons.lua
90
polygons.lua
@ -1,94 +1,20 @@
|
|||||||
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 blocksize = mapgen_rivers.settings.blocksize
|
local blocksize = mapgen_rivers.settings.blocksize
|
||||||
local X = math.floor(mapgen_rivers.settings.map_x_size / blocksize)
|
local X = math.floor(mapgen_rivers.settings.map_x_size / blocksize)
|
||||||
local Z = math.floor(mapgen_rivers.settings.map_z_size / blocksize)
|
local Z = math.floor(mapgen_rivers.settings.map_z_size / blocksize)
|
||||||
|
|
||||||
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 function index(x, z)
|
local function index(x, z)
|
||||||
return z*X+x+1
|
return z*X+x+1
|
||||||
end
|
end
|
||||||
|
|
||||||
local min_catchment = mapgen_rivers.settings.min_catchment
|
|
||||||
local max_catchment = mapgen_rivers.settings.max_catchment
|
|
||||||
|
|
||||||
local map_offset = {x=0, z=0}
|
local map_offset = {x=0, z=0}
|
||||||
if mapgen_rivers.settings.center then
|
mapgen_rivers.register_on_grid_loaded(function(grid)
|
||||||
map_offset.x = blocksize*X/2
|
X = grid.size.x
|
||||||
map_offset.z = blocksize*Z/2
|
Z = grid.size.y
|
||||||
end
|
if mapgen_rivers.settings.center then
|
||||||
|
map_offset.x = blocksize*X/2
|
||||||
|
map_offset.z = blocksize*Z/2
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
-- Localize for performance
|
-- Localize for performance
|
||||||
local floor, ceil, min, max, abs = math.floor, math.ceil, math.min, math.max, math.abs
|
local floor, ceil, min, max, abs = math.floor, math.ceil, math.min, math.max, math.abs
|
||||||
|
@ -46,8 +46,7 @@ local function margin(dem, width, elev)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function pregenerate(keep_loaded)
|
function mapgen_rivers.pregenerate(grid)
|
||||||
local grid = mapgen_rivers.grid
|
|
||||||
local size = grid.size
|
local size = grid.size
|
||||||
|
|
||||||
if size.x * size.y > 4e6 then
|
if size.x * size.y > 4e6 then
|
||||||
@ -101,25 +100,14 @@ local function pregenerate(keep_loaded)
|
|||||||
offset_y[i] = mmin(mmax(offset_y[i]*256, -128), 127)
|
offset_y[i] = mmin(mmax(offset_y[i]*256, -128), 127)
|
||||||
end
|
end
|
||||||
|
|
||||||
mapgen_rivers.write_map('dem', model.dem, 2)
|
grid.dem = model.dem
|
||||||
mapgen_rivers.write_map('lakes', model.lakes, 2)
|
grid.lakes = model.lakes
|
||||||
mapgen_rivers.write_map('dirs', model.dirs, 1)
|
grid.dirs = model.dirs
|
||||||
mapgen_rivers.write_map('rivers', model.rivers, 4)
|
grid.rivers = model.rivers
|
||||||
mapgen_rivers.write_map('offset_x', offset_x, 1)
|
grid.offset_x = offset_x
|
||||||
mapgen_rivers.write_map('offset_y', offset_y, 1)
|
grid.offset_y = offset_y
|
||||||
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.load_method = "full"
|
||||||
grid.dem = model.dem
|
grid.conv_applied = false
|
||||||
grid.lakes = model.lakes
|
|
||||||
grid.dirs = model.dirs
|
|
||||||
grid.rivers = model.rivers
|
|
||||||
grid.offset_x = offset_x
|
|
||||||
grid.offset_y = offset_y
|
|
||||||
end
|
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
end
|
end
|
||||||
|
|
||||||
return pregenerate
|
|
||||||
|
@ -111,6 +111,9 @@ mapgen_rivers.settings = {
|
|||||||
load_all = mtsettings:get_bool('mapgen_rivers_load_all')
|
load_all = mtsettings:get_bool('mapgen_rivers_load_all')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mapgen_rivers.settings.load_method =
|
||||||
|
mapgen_rivers.settings.load_all and "full" or "interactive"
|
||||||
|
|
||||||
mapgen_rivers.noise_params = {
|
mapgen_rivers.noise_params = {
|
||||||
base = def_setting("np_base", "noise"),
|
base = def_setting("np_base", "noise"),
|
||||||
distort_x = def_setting("np_distort_x", "noise"),
|
distort_x = def_setting("np_distort_x", "noise"),
|
||||||
|
Loading…
Reference in New Issue
Block a user