From 6564d40b857fe808f18d18c9d4f79c6e3ff878fc Mon Sep 17 00:00:00 2001 From: Gael-de-Sailly Date: Tue, 25 Jan 2022 19:09:24 +0100 Subject: [PATCH] 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 --- load.lua => gridio.lua | 26 +++- gridmanager.lua | 146 ++++++++++++++++++++++ init.lua | 274 ++--------------------------------------- mapgen.lua | 269 ++++++++++++++++++++++++++++++++++++++++ polygons.lua | 90 ++------------ pregenerate.lua | 30 ++--- settings.lua | 3 + 7 files changed, 465 insertions(+), 373 deletions(-) rename load.lua => gridio.lua (72%) create mode 100644 gridmanager.lua create mode 100644 mapgen.lua diff --git a/load.lua b/gridio.lua similarity index 72% rename from load.lua rename to gridio.lua index 2209fbf..4e1271b 100644 --- a/load.lua +++ b/gridio.lua @@ -4,7 +4,8 @@ local floor = math.floor local sbyte, schar = string.byte, string.char 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 data = file:read('*all') if #data < bytes*size then @@ -62,18 +63,31 @@ local loader_mt = { 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') 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 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 file = io.open(worldpath .. filename, 'wb') diff --git a/gridmanager.lua b/gridmanager.lua new file mode 100644 index 0000000..84e00e2 --- /dev/null +++ b/gridmanager.lua @@ -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 + diff --git a/init.lua b/init.lua index 8cb43b7..b1ea6d0 100644 --- a/init.lua +++ b/init.lua @@ -4,275 +4,21 @@ 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') +dofile(modpath .. 'gridmanager.lua') +dofile(modpath .. 'gridio.lua') dofile(modpath .. 'polygons.lua') dofile(modpath .. 'heightmap.lua') +dofile(modpath .. 'mapgen.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 +minetest.register_on_mods_loaded(function() + local exist = mapgen_rivers.try_load_grid() -if use_biomegen_mod then - biomegen.set_elevation_chill(elevation_chill) -end + if not exist then -- If grid does not exist yet, generate it + dofile(modpath .. 'pregenerate.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 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 + local grid = mapgen_rivers.generate_grid() + mapgen_rivers.write_grid(grid) + mapgen_rivers.try_load_grid(grid) -- Reload if needed 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) diff --git a/mapgen.lua b/mapgen.lua new file mode 100644 index 0000000..d565ea9 --- /dev/null +++ b/mapgen.lua @@ -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) diff --git a/polygons.lua b/polygons.lua index b977958..665b728 100644 --- a/polygons.lua +++ b/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 X = math.floor(mapgen_rivers.settings.map_x_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) return z*X+x+1 end -local min_catchment = mapgen_rivers.settings.min_catchment -local max_catchment = mapgen_rivers.settings.max_catchment - local map_offset = {x=0, z=0} -if mapgen_rivers.settings.center then - map_offset.x = blocksize*X/2 - map_offset.z = blocksize*Z/2 -end +mapgen_rivers.register_on_grid_loaded(function(grid) + X = grid.size.x + Z = grid.size.y + if mapgen_rivers.settings.center then + map_offset.x = blocksize*X/2 + map_offset.z = blocksize*Z/2 + end +end) -- Localize for performance local floor, ceil, min, max, abs = math.floor, math.ceil, math.min, math.max, math.abs diff --git a/pregenerate.lua b/pregenerate.lua index 30ed915..7b2d1b4 100644 --- a/pregenerate.lua +++ b/pregenerate.lua @@ -46,8 +46,7 @@ local function margin(dem, width, elev) end end -local function pregenerate(keep_loaded) - local grid = mapgen_rivers.grid +function mapgen_rivers.pregenerate(grid) local size = grid.size 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) 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() + 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 - 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 + grid.load_method = "full" + grid.conv_applied = false collectgarbage() end - -return pregenerate diff --git a/settings.lua b/settings.lua index c5e66f8..a508b30 100644 --- a/settings.lua +++ b/settings.lua @@ -111,6 +111,9 @@ mapgen_rivers.settings = { 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 = { base = def_setting("np_base", "noise"), distort_x = def_setting("np_distort_x", "noise"),