mirror of
https://gitlab.com/gaelysam/mapgen_rivers.git
synced 2025-01-01 05:50:34 +01:00
Started refactoring code structure to support a separate mapgen thread
Probably not working for now (untested), still some things to do Also making the code cleaner
This commit is contained in:
parent
1ad8c96b8c
commit
11e6e72324
286
init.lua
286
init.lua
@ -11,269 +11,31 @@ end
|
||||
|
||||
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(mapgen_rivers.world_data_path .. '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)
|
||||
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 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))
|
||||
mapgen_rivers.use_mapgen_thread = minetest.settings:get_bool("mapgen_rivers_use_mapgen_thread")
|
||||
mapgen_rivers.thread = 'main'
|
||||
if mapgen_rivers.use_mapgen_thread and minetest.register_mapgen_dofile then
|
||||
minetest.register_mapgen_dofile(modpath .. 'mapgen.lua')
|
||||
else
|
||||
minetest.log("warning", "[mapgen_rivers] Mapgen thread not available on this Minetest version.")
|
||||
mapgen_rivers.use_mapgen_thread = false
|
||||
dofile(modpath .. 'mapgen.lua')
|
||||
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)
|
||||
-- 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
|
100
load_grid.lua
Normal file
100
load_grid.lua
Normal file
@ -0,0 +1,100 @@
|
||||
local worldpath = mapgen_rivers.world_data_path
|
||||
|
||||
local floor = math.floor
|
||||
local sbyte = string.byte
|
||||
local unpk = unpack
|
||||
|
||||
local load_map
|
||||
|
||||
if mapgen_rivers.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(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
|
||||
|
||||
else
|
||||
|
||||
load_map = function(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
|
||||
end
|
||||
|
||||
local sfile = io.open(world_data_path..'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);
|
||||
}
|
295
mapgen.lua
Normal file
295
mapgen.lua
Normal file
@ -0,0 +1,295 @@
|
||||
-- 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'}
|
||||
end
|
||||
|
||||
if not mapgen_rivers.grid then
|
||||
dofile(minetest.get_modpath(minetest.get_current_modname()) .. '/load_grid.lua')
|
||||
end
|
||||
|
||||
local settings = Settings(minetest.get_worldpath() .. '/mapgen_rivers.conf')
|
||||
|
||||
local sea_level = tonumber(settings:get('sea_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.global_exists('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("np_heat")
|
||||
noiseparams.heat_blend = minetest.get_mapgen_setting_noiseparams("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(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 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()
|
||||
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)
|
79
polygons.lua
79
polygons.lua
@ -1,80 +1,5 @@
|
||||
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
|
||||
|
137
pregenerate.lua
137
pregenerate.lua
@ -13,69 +13,100 @@ local time_step = mapgen_rivers.settings.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 grid = mapgen_rivers.grid
|
||||
local size = grid.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
|
||||
minetest.mkdir(mapgen_rivers.world_data_path)
|
||||
|
||||
local function 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
|
||||
|
||||
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(mapgen_rivers.world_data_path .. 'size', "w")
|
||||
sfile:write(size.x..'\n'..size.y)
|
||||
sfile:close()
|
||||
|
||||
if not mapgen_rivers.settings.use_interactive_loader then
|
||||
mapgen_rivers.grid = {
|
||||
size = {x=size_x, y=size_y},
|
||||
dem = model.dem,
|
||||
lakes = model.lakes,
|
||||
dirs = model.dirs,
|
||||
rivers = model.rivers,
|
||||
offset_x = offset_x,
|
||||
offset_y = offset_y,
|
||||
}
|
||||
end
|
||||
|
@ -87,9 +87,6 @@ mapgen_rivers.settings = {
|
||||
load_all = mtsettings:get_bool('mapgen_rivers_load_all')
|
||||
}
|
||||
|
||||
local function write_settings()
|
||||
mgrsettings:write()
|
||||
end
|
||||
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/noises.lua")
|
||||
|
||||
minetest.register_on_mods_loaded(write_settings)
|
||||
minetest.register_on_shutdown(write_settings)
|
||||
mgrsettings:write()
|
||||
|
Loading…
Reference in New Issue
Block a user