From 55725ad94b11bb320d70e51ea462affd1b827cb9 Mon Sep 17 00:00:00 2001 From: Gael-de-Sailly Date: Tue, 14 Apr 2020 21:11:54 +0200 Subject: [PATCH] Re-organized the code. All polygon-related calculations go to polygons.lua. --- geometry.lua | 5 +- init.lua | 175 +++------------------------------------------------ load.lua | 4 +- polygons.lua | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ settings.lua | 6 +- 5 files changed, 185 insertions(+), 171 deletions(-) create mode 100644 polygons.lua diff --git a/geometry.lua b/geometry.lua index 7a35b0a..773a39f 100644 --- a/geometry.lua +++ b/geometry.lua @@ -28,7 +28,4 @@ local function transform_quadri(X, Y, x, y) return xc, yc end -return { - distance_to_segment = distance_to_segment, - transform_quadri = transform_quadri, -} +return transform_quadri diff --git a/init.lua b/init.lua index 5ae0e98..949402e 100644 --- a/init.lua +++ b/init.lua @@ -1,53 +1,16 @@ +mapgen_rivers = {} + local modpath = minetest.get_modpath(minetest.get_current_modname()) .. '/' -local worldpath = minetest.get_worldpath() .. '/' -local load_map = dofile(modpath .. 'load.lua') -local geometry = dofile(modpath .. 'geometry.lua') -local function copy_if_needed(filename) - local wfilename = worldpath..filename - local wfile = io.open(wfilename, 'r') - if wfile then - wfile:close() - return - end - local mfilename = modpath..filename - local mfile = io.open(mfilename, 'r') - local wfile = io.open(wfilename, 'w') - wfile:write(mfile:read("*all")) - mfile:close() - wfile:close() -end +dofile(modpath .. 'settings.lua') -copy_if_needed('size') -local sfile = io.open(worldpath..'size') -local X = tonumber(sfile:read('*l')) -local Z = tonumber(sfile:read('*l')) +local blocksize = mapgen_rivers.blocksize +local sea_level = mapgen_rivers.sea_level +local riverbed_slope = mapgen_rivers.riverbed_slope -copy_if_needed('dem') -local dem = load_map(worldpath..'dem', 2, true, X*Z) -copy_if_needed('lakes') -local lakes = load_map(worldpath..'lakes', 2, true, X*Z) -copy_if_needed('bounds_x') -local bounds_x = load_map(worldpath..'bounds_x', 4, false, (X-1)*Z) -copy_if_needed('bounds_y') -local bounds_z = load_map(worldpath..'bounds_y', 4, false, X*(Z-1)) +local make_polygons = dofile(modpath .. 'polygons.lua') -copy_if_needed('offset_x') -local offset_x = load_map(worldpath..'offset_x', 1, true, X*Z) -for k, v in ipairs(offset_x) do - offset_x[k] = (v+0.5)/256 -end - -copy_if_needed('offset_y') -local offset_z = load_map(worldpath..'offset_y', 1, true, X*Z) -for k, v in ipairs(offset_z) do - offset_z[k] = (v+0.5)/256 -end - - -local function index(x, z) - return z*X+x+1 -end +local transform_quadri = dofile(modpath .. 'geometry.lua') local function interp(v00, v01, v11, v10, xf, zf) local v0 = v01*xf + v00*(1-xf) @@ -57,34 +20,6 @@ end local data = {} -local blocksize = 12 -local sea_level = 1 -local min_catchment = 25 -local max_catchment = 40000 -local riverbed_slope = 0.4 - -local get_settings = dofile(modpath .. 'settings.lua') - -blocksize = get_settings('blocksize', 'int', blocksize) -sea_level = get_settings('sea_level', 'int', sea_level) -min_catchment = get_settings('min_catchment', 'float', min_catchment) -max_catchment = get_settings('max_catchment', 'float', max_catchment) -riverbed_slope = get_settings('riverbed_slope', 'float', riverbed_slope) * blocksize - --- Width coefficients: coefficients solving --- wfactor * min_catchment ^ wpower = 1/(2*blocksize) --- wfactor * max_catchment ^ wpower = 1 -local wpower = math.log(2*blocksize)/math.log(max_catchment/min_catchment) -local wfactor = 1 / max_catchment ^ wpower -local function river_width(flow) - flow = math.abs(flow) - if flow < min_catchment then - return 0 - end - - return math.min(wfactor * flow ^ wpower, 1) -end - local function generate(minp, maxp, seed) local c_stone = minetest.get_content_id("default:stone") local c_dirt = minetest.get_content_id("default:dirt") @@ -98,105 +33,15 @@ local function generate(minp, maxp, seed) 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 chulens = maxp.z - minp.z + 1 - local polygons = {} - local xpmin, xpmax = math.max(math.floor(minp.x/blocksize - 0.5), 0), math.min(math.ceil(maxp.x/blocksize), X-2) - local zpmin, zpmax = math.max(math.floor(minp.z/blocksize - 0.5), 0), math.min(math.ceil(maxp.z/blocksize), Z-2) - - for xp = xpmin, xpmax do - for zp=zpmin, zpmax do - local iA = index(xp, zp) - local iB = index(xp+1, zp) - local iC = index(xp+1, zp+1) - local iD = index(xp, zp+1) - local poly_x = {offset_x[iA]+xp, offset_x[iB]+xp+1, offset_x[iC]+xp+1, offset_x[iD]+xp} - local poly_z = {offset_z[iA]+zp, offset_z[iB]+zp, offset_z[iC]+zp+1, offset_z[iD]+zp+1} - local polygon = {x=poly_x, z=poly_z, i={iA, iB, iC, iD}} - - local bounds = {} - local xmin = math.max(math.floor(blocksize*math.min(unpack(poly_x)))+1, minp.x) - local xmax = math.min(math.floor(blocksize*math.max(unpack(poly_x))), maxp.x) - for x=xmin, xmax do - bounds[x] = {} - end - - local i1 = 4 - for i2=1, 4 do -- Loop on 4 edges - local x1, x2 = poly_x[i1], poly_x[i2] - local lxmin = math.floor(blocksize*math.min(x1, x2))+1 - local lxmax = math.floor(blocksize*math.max(x1, x2)) - if lxmin <= lxmax then - local z1, z2 = poly_z[i1], poly_z[i2] - local a = (z1-z2) / (x1-x2) - local b = blocksize*(z1 - a*x1) - for x=math.max(lxmin, minp.x), math.min(lxmax, maxp.x) do - table.insert(bounds[x], a*x+b) - end - end - i1 = i2 - end - for x=xmin, xmax do - local xlist = bounds[x] - table.sort(xlist) - local c = math.floor(#xlist/2) - for l=1, c do - local zmin = math.max(math.floor(xlist[l*2-1])+1, minp.z) - local zmax = math.min(math.floor(xlist[l*2]), maxp.z) - local i = (x-minp.x) * chulens + (zmin-minp.z) + 1 - for z=zmin, zmax do - polygons[i] = polygon - i = i + 1 - end - end - end - - polygon.dem = {dem[iA], dem[iB], dem[iC], dem[iD]} - polygon.lake = math.min(lakes[iA], lakes[iB], lakes[iC], lakes[iD]) - - local river_west = river_width(bounds_z[iA]) - local river_north = river_width(bounds_x[iA-zp]) - local river_east = 1-river_width(bounds_z[iB]) - local river_south = 1-river_width(bounds_x[iD-zp-1]) - if river_west > river_east then - local mean = (river_west + river_east) / 2 - river_west = mean - river_east = mean - end - if river_north > river_south then - local mean = (river_north + river_south) / 2 - river_north = mean - river_south = mean - end - polygon.rivers = {river_west, river_north, river_east, river_south} - - local around = {0,0,0,0,0,0,0,0} - if zp > 0 then - around[1] = river_width(bounds_z[iA-X]) - around[2] = river_width(bounds_z[iB-X]) - end - if xp < X-2 then - around[3] = river_width(bounds_x[iB-zp]) - around[4] = river_width(bounds_x[iC-zp-1]) - end - if zp < Z-2 then - around[5] = river_width(bounds_z[iC]) - around[6] = river_width(bounds_z[iD]) - end - if xp > 0 then - around[7] = river_width(bounds_x[iD-zp-2]) - around[8] = river_width(bounds_x[iA-zp-1]) - end - polygon.river_corners = {math.max(around[8], around[1]), math.max(around[2], around[3]), math.max(around[4], around[5]), math.max(around[6], around[7])} - end - end + local polygons = make_polygons(minp, maxp) local i = 1 for x = minp.x, maxp.x do for z = minp.z, maxp.z do local poly = polygons[i] if poly then - local xf, zf = geometry.transform_quadri(poly.x, poly.z, x/blocksize, z/blocksize) + local xf, zf = transform_quadri(poly.x, poly.z, x/blocksize, z/blocksize) local i00, i01, i11, i10 = unpack(poly.i) local is_river = false diff --git a/load.lua b/load.lua index df1db3e..b502567 100644 --- a/load.lua +++ b/load.lua @@ -1,5 +1,7 @@ +local worldpath = minetest.get_worldpath() .. "/" + local function load_map(filename, bytes, signed, size) - local file = io.open(filename, 'r') + local file = io.open(worldpath .. filename, 'r') local data = file:read('*all') if #data < bytes*size then data = minetest.decompress(data) diff --git a/polygons.lua b/polygons.lua new file mode 100644 index 0000000..22eae57 --- /dev/null +++ b/polygons.lua @@ -0,0 +1,166 @@ +local modpath = minetest.get_modpath(minetest.get_current_modname()) .. '/' +local worldpath = minetest.get_worldpath() .. '/' +local load_map = dofile(modpath .. 'load.lua') + +local function copy_if_needed(filename) + local wfilename = worldpath..filename + local wfile = io.open(wfilename, 'r') + if wfile then + wfile:close() + return + end + local mfilename = modpath..filename + local mfile = io.open(mfilename, 'r') + local wfile = io.open(wfilename, 'w') + wfile:write(mfile:read("*all")) + mfile:close() + wfile:close() +end + +copy_if_needed('size') +local sfile = io.open(worldpath..'size') +local X = tonumber(sfile:read('*l')) +local Z = tonumber(sfile:read('*l')) +sfile:close() + +copy_if_needed('dem') +local dem = load_map('dem', 2, true, X*Z) +copy_if_needed('lakes') +local lakes = load_map('lakes', 2, true, X*Z) +copy_if_needed('bounds_x') +local bounds_x = load_map('bounds_x', 4, false, (X-1)*Z) +copy_if_needed('bounds_y') +local bounds_z = load_map('bounds_y', 4, false, X*(Z-1)) + +copy_if_needed('offset_x') +local offset_x = load_map('offset_x', 1, true, X*Z) +for k, v in ipairs(offset_x) do + offset_x[k] = (v+0.5)/256 +end + +copy_if_needed('offset_y') +local offset_z = load_map('offset_y', 1, true, X*Z) +for k, v in ipairs(offset_z) do + offset_z[k] = (v+0.5)/256 +end + +local function index(x, z) + return z*X+x+1 +end + +local blocksize = mapgen_rivers.blocksize +local min_catchment = mapgen_rivers.min_catchment +local max_catchment = mapgen_rivers.max_catchment + +-- Width coefficients: coefficients solving +-- wfactor * min_catchment ^ wpower = 1/(2*blocksize) +-- wfactor * max_catchment ^ wpower = 1 +local wpower = math.log(2*blocksize)/math.log(max_catchment/min_catchment) +local wfactor = 1 / max_catchment ^ wpower +local function river_width(flow) + flow = math.abs(flow) + if flow < min_catchment then + return 0 + end + + return math.min(wfactor * flow ^ wpower, 1) +end + +local function make_polygons(minp, maxp) + local chulens = maxp.z - minp.z + 1 + + local polygons = {} + local xpmin, xpmax = math.max(math.floor(minp.x/blocksize - 0.5), 0), math.min(math.ceil(maxp.x/blocksize), X-2) + local zpmin, zpmax = math.max(math.floor(minp.z/blocksize - 0.5), 0), math.min(math.ceil(maxp.z/blocksize), Z-2) + + for xp = xpmin, xpmax do + for zp=zpmin, zpmax do + local iA = index(xp, zp) + local iB = index(xp+1, zp) + local iC = index(xp+1, zp+1) + local iD = index(xp, zp+1) + local poly_x = {offset_x[iA]+xp, offset_x[iB]+xp+1, offset_x[iC]+xp+1, offset_x[iD]+xp} + local poly_z = {offset_z[iA]+zp, offset_z[iB]+zp, offset_z[iC]+zp+1, offset_z[iD]+zp+1} + local polygon = {x=poly_x, z=poly_z, i={iA, iB, iC, iD}} + + local bounds = {} + local xmin = math.max(math.floor(blocksize*math.min(unpack(poly_x)))+1, minp.x) + local xmax = math.min(math.floor(blocksize*math.max(unpack(poly_x))), maxp.x) + for x=xmin, xmax do + bounds[x] = {} + end + + local i1 = 4 + for i2=1, 4 do -- Loop on 4 edges + local x1, x2 = poly_x[i1], poly_x[i2] + local lxmin = math.floor(blocksize*math.min(x1, x2))+1 + local lxmax = math.floor(blocksize*math.max(x1, x2)) + if lxmin <= lxmax then + local z1, z2 = poly_z[i1], poly_z[i2] + local a = (z1-z2) / (x1-x2) + local b = blocksize*(z1 - a*x1) + for x=math.max(lxmin, minp.x), math.min(lxmax, maxp.x) do + table.insert(bounds[x], a*x+b) + end + end + i1 = i2 + end + for x=xmin, xmax do + local xlist = bounds[x] + table.sort(xlist) + local c = math.floor(#xlist/2) + for l=1, c do + local zmin = math.max(math.floor(xlist[l*2-1])+1, minp.z) + local zmax = math.min(math.floor(xlist[l*2]), maxp.z) + local i = (x-minp.x) * chulens + (zmin-minp.z) + 1 + for z=zmin, zmax do + polygons[i] = polygon + i = i + 1 + end + end + end + + polygon.dem = {dem[iA], dem[iB], dem[iC], dem[iD]} + polygon.lake = math.min(lakes[iA], lakes[iB], lakes[iC], lakes[iD]) + + local river_west = river_width(bounds_z[iA]) + local river_north = river_width(bounds_x[iA-zp]) + local river_east = 1-river_width(bounds_z[iB]) + local river_south = 1-river_width(bounds_x[iD-zp-1]) + if river_west > river_east then + local mean = (river_west + river_east) / 2 + river_west = mean + river_east = mean + end + if river_north > river_south then + local mean = (river_north + river_south) / 2 + river_north = mean + river_south = mean + end + polygon.rivers = {river_west, river_north, river_east, river_south} + + local around = {0,0,0,0,0,0,0,0} + if zp > 0 then + around[1] = river_width(bounds_z[iA-X]) + around[2] = river_width(bounds_z[iB-X]) + end + if xp < X-2 then + around[3] = river_width(bounds_x[iB-zp]) + around[4] = river_width(bounds_x[iC-zp-1]) + end + if zp < Z-2 then + around[5] = river_width(bounds_z[iC]) + around[6] = river_width(bounds_z[iD]) + end + if xp > 0 then + around[7] = river_width(bounds_x[iD-zp-2]) + around[8] = river_width(bounds_x[iA-zp-1]) + end + polygon.river_corners = {math.max(around[8], around[1]), math.max(around[2], around[3]), math.max(around[4], around[5]), math.max(around[6], around[7])} + end + end + + return polygons +end + +return make_polygons diff --git a/settings.lua b/settings.lua index a38d097..566439b 100644 --- a/settings.lua +++ b/settings.lua @@ -38,4 +38,8 @@ local function get_settings(key, dtype, default) end end -return get_settings +mapgen_rivers.blocksize = get_settings('blocksize', 'int', 12) +mapgen_rivers.sea_level = get_settings('sea_level', 'int', 1) +mapgen_rivers.min_catchment = get_settings('min_catchment', 'float', 25) +mapgen_rivers.max_catchment = get_settings('max_catchment', 'float', 40000) +mapgen_rivers.riverbed_slope = get_settings('riverbed_slope', 'float', 0.4) * mapgen_rivers.blocksize