diff --git a/init.lua b/init.lua index 9bc3084..4c6e3b6 100644 --- a/init.lua +++ b/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) diff --git a/load.lua b/load.lua deleted file mode 100644 index 2209fbf..0000000 --- a/load.lua +++ /dev/null @@ -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 diff --git a/load_grid.lua b/load_grid.lua new file mode 100644 index 0000000..3f3af47 --- /dev/null +++ b/load_grid.lua @@ -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); +} diff --git a/mapgen.lua b/mapgen.lua new file mode 100644 index 0000000..c4accdc --- /dev/null +++ b/mapgen.lua @@ -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) diff --git a/polygons.lua b/polygons.lua index c531b2b..941fca6 100644 --- a/polygons.lua +++ b/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 diff --git a/pregenerate.lua b/pregenerate.lua index 184482c..51ae40c 100644 --- a/pregenerate.lua +++ b/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 diff --git a/settings.lua b/settings.lua index 7714a11..579cd1a 100644 --- a/settings.lua +++ b/settings.lua @@ -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()