Implemented grid twisting. Still many possible bugs, potentially clumsy implementation, but it seems to work.

This commit is contained in:
Gael-de-Sailly 2020-04-12 09:40:10 +02:00
parent c8139de6d7
commit e227755531
5 changed files with 134 additions and 12 deletions

2
.gitignore vendored
View File

@ -4,4 +4,6 @@ lakes
links links
rivers rivers
size size
offset_x
offset_y
unused/ unused/

45
geometry.lua Normal file
View File

@ -0,0 +1,45 @@
local function distance_to_segment(x1, y1, x2, y2, x, y)
-- get the distance between point (x,y) and segment (x1,y1)-(x2,y2)
local a = (x1-x2)^2 + (y1-y2)^2
local b = (x1-x)^2 + (y1-y)^2
local c = (x2-x)^2 + (y2-y)^2
if a + b < c then
return math.sqrt(b)
elseif a + c < b then
return math.sqrt(c)
else
return math.abs(x1 * (y2-y) + x2 * (y-y1) + x * (y1-y2)) / math.sqrt(a)
end
end
local function transform_quadri(X, Y, x, y)
-- X, Y 4-vectors giving the coordinates of the 4 nodes
-- x, y position to index.
local x1, x2, x3, x4 = unpack(X)
local y1, y2, y3, y4 = unpack(Y)
local d12 = distance_to_segment(x1,y1,x2,y2,x,y)
local d34 = distance_to_segment(x3,y3,x4,y4,x,y)
local xc = d12 / (d12+d34)
local d23 = distance_to_segment(x2,y2,x3,y3,x,y)
local d41 = distance_to_segment(x4,y4,x1,y1,x,y)
local yc = d41 / (d23+d41)
return xc, yc
end
local function area(X, Y) -- Signed area of polygon, in function of direction of rotation. Clockwise = positive.
local n = #X
local sum = X[1]*Y[n] - X[n]*Y[1]
for i=2, n do
sum = sum + X[i]*Y[i-1] - X[i-1]*Y[i]
end
return sum/2
end
return {
distance_to_segment = distance_to_segment,
transform_quadri = transform_quadri,
area = area,
}

View File

@ -1,6 +1,7 @@
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. '/' local modpath = minetest.get_modpath(minetest.get_current_modname()) .. '/'
local worldpath = minetest.get_worldpath() .. '/' local worldpath = minetest.get_worldpath() .. '/'
local load_map = dofile(modpath .. 'load.lua') local load_map = dofile(modpath .. 'load.lua')
local geometry = dofile(modpath .. 'geometry.lua')
local function copy_if_needed(filename) local function copy_if_needed(filename)
local wfilename = worldpath..filename local wfilename = worldpath..filename
@ -31,19 +32,37 @@ local links = load_map(worldpath..'links', 1, false)
copy_if_needed('rivers') copy_if_needed('rivers')
local rivers = load_map(worldpath..'rivers', 4, false) local rivers = load_map(worldpath..'rivers', 4, false)
copy_if_needed('offset_x')
local offset_x = load_map(worldpath..'offset_x', 1, true)
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)
for k, v in ipairs(offset_z) do
offset_z[k] = (v+0.5)/256
end
local function index(x, z) local function index(x, z)
return z*X+x+1 return z*X+x+1
end end
local function get_point_location(x, z)
local i = index(x, z)
return x+offset_x[i], z+offset_z[i]
end
local function interp(v00, v01, v10, v11, xf, zf) local function interp(v00, v01, v10, v11, xf, zf)
v0 = v01*xf + v00*(1-xf) local v0 = v01*xf + v00*(1-xf)
v1 = v11*xf + v10*(1-xf) local v1 = v11*xf + v10*(1-xf)
return v1*zf + v0*(1-zf) return v1*zf + v0*(1-zf)
end end
local data = {} local data = {}
local blocksize = 6 local blocksize = 20
local sea_level = 1 local sea_level = 1
local min_catchment = 25 local min_catchment = 25
@ -82,15 +101,62 @@ local function generate(minp, maxp, seed)
for z = minp.z, maxp.z do for z = minp.z, maxp.z do
local xb = x/blocksize local xb = x/blocksize
local zb = z/blocksize local zb = z/blocksize
local xc = math.floor(xb+0.5)
local zc = math.floor(zb+0.5)
if xb >= 0 and xb < X-1 and zb >= 0 and zb < Z-1 then local x0, z0
local x0 = math.floor(xb) if xc >= 0 and zc >= 0 and xc < X and zc < Z then
local xoff, zoff = get_point_location(xc, zc)
local north, east, south, west
if xc > 0 then
local x1off, z1off = get_point_location(xc-1, zc)
west = geometry.area({xoff, x1off, xb}, {zoff, z1off, zb}) <= 0
else
west = zb > zoff
end
if zc > 0 then
local x2off, z2off = get_point_location(xc, zc-1)
north = geometry.area({xoff, x2off, xb}, {zoff, z2off, zb}) <= 0
else
north = xb > xoff
end
if xc < X-1 then
local x3off, z3off = get_point_location(xc+1, zc)
east = geometry.area({xoff, x3off, xb}, {zoff, z3off, zb}) <= 0
else
east = zb < zoff
end
if zc < Z-1 then
local x4off, z4off = get_point_location(xc, zc+1)
south = geometry.area({xoff, x4off, xb}, {zoff, z4off, zb}) <= 0
else
south = xb < xoff
end
if west and not north then
x0, z0 = xc-1, zc-1
elseif north and not east then
x0, z0 = xc, zc-1
elseif east and not south then
x0, z0 = xc, zc
elseif south and not west then
x0, z0 = xc-1, zc
else
x0, z0 = xc, zc
end
end
if x0 and z0 and x0 >= 0 and x0 < X-1 and z0 >= 0 and z0 < Z-1 then
local x1 = x0+1 local x1 = x0+1
local z0 = math.floor(zb)
local z1 = z0+1 local z1 = z0+1
local xf, zf
local xf = xb - x0 do
local zf = zb - z0 local xA, zA = get_point_location(x0, z0)
local xB, zB = get_point_location(x0, z1)
local xC, zC = get_point_location(x1, z1)
local xD, zD = get_point_location(x1, z0)
xf, zf = geometry.transform_quadri({xA, xB, xC, xD}, {zA, zB, zC, zD}, xb, zb)
end
local i00 = index(x0,z0) local i00 = index(x0,z0)
local i01 = index(x1,z0) local i01 = index(x1,z0)
@ -115,7 +181,7 @@ local function generate(minp, maxp, seed)
local is_lake = lake_height > terrain_height local is_lake = lake_height > terrain_height
local is_river = false local is_river = false
if xf == 0 then if xf < 1/6 then
if links[i00] == 1 and rivers[i00] >= min_catchment then if links[i00] == 1 and rivers[i00] >= min_catchment then
is_river = true is_river = true
elseif links[i10] == 3 and rivers[i10] >= min_catchment then elseif links[i10] == 3 and rivers[i10] >= min_catchment then
@ -123,7 +189,7 @@ local function generate(minp, maxp, seed)
end end
end end
if zf == 0 then if zf < 1/6 then
if links[i00] == 2 and rivers[i00] >= min_catchment then if links[i00] == 2 and rivers[i00] >= min_catchment then
is_river = true is_river = true
elseif links[i01] == 4 and rivers[i01] >= min_catchment then elseif links[i01] == 4 and rivers[i01] >= min_catchment then

View File

@ -18,7 +18,7 @@ neighbours_dirs = np.array([
neighbours_pattern = neighbours_dirs > 0 neighbours_pattern = neighbours_dirs > 0
def flow_dirs_lakes(dem, random=0.0625): def flow_dirs_lakes(dem, random=0):
(Y, X) = dem.shape (Y, X) = dem.shape
dem_margin = np.zeros((Y+2, X+2)) dem_margin = np.zeros((Y+2, X+2))

View File

@ -4,6 +4,7 @@ import numpy as np
import noise import noise
from save import save from save import save
from erosion import EvolutionModel from erosion import EvolutionModel
import bounds
import os import os
import sys import sys
@ -67,10 +68,18 @@ model.calculate_flow()
print('Done') print('Done')
bx, by = bounds.make_bounds(model.dirs, model.rivers)
ox, oy = bounds.twist(bx, by, bounds.get_fixed(model.dirs))
offset_x = np.clip(np.floor(ox * 256), -128, 127)
offset_y = np.clip(np.floor(oy * 256), -128, 127)
save(model.dem, 'dem', dtype='>i2') save(model.dem, 'dem', dtype='>i2')
save(model.lakes, 'lakes', dtype='>i2') save(model.lakes, 'lakes', dtype='>i2')
save(model.dirs, 'links', dtype='u1') save(model.dirs, 'links', dtype='u1')
save(model.rivers, 'rivers', dtype='>u4') save(model.rivers, 'rivers', dtype='>u4')
save(offset_x, 'offset_x', dtype='i1')
save(offset_y, 'offset_y', dtype='i1')
with open('size', 'w') as sfile: with open('size', 'w') as sfile:
sfile.write('{:d}\n{:d}'.format(mapsize, mapsize)) sfile.write('{:d}\n{:d}'.format(mapsize, mapsize))