From b7c6f716355011764949ee70a0e61b0e0de21c1a Mon Sep 17 00:00:00 2001 From: Gael-de-Sailly Date: Sun, 12 Apr 2020 09:40:10 +0200 Subject: [PATCH] Implemented grid twisting. Still many possible bugs, potentially clumsy implementation, but it seems to work. --- .gitignore | 2 ++ geometry.lua | 45 ++++++++++++++++++++++++ init.lua | 88 +++++++++++++++++++++++++++++++++++++++++------ rivermapper.py | 2 +- terrain_rivers.py | 9 +++++ 5 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 geometry.lua diff --git a/.gitignore b/.gitignore index e257aae..e7cb97b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ lakes links rivers size +offset_x +offset_y unused/ diff --git a/geometry.lua b/geometry.lua new file mode 100644 index 0000000..ffa2a7b --- /dev/null +++ b/geometry.lua @@ -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, +} diff --git a/init.lua b/init.lua index 8f934b0..7b7f7c1 100644 --- a/init.lua +++ b/init.lua @@ -1,6 +1,7 @@ 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 @@ -31,19 +32,37 @@ local links = load_map(worldpath..'links', 1, false) copy_if_needed('rivers') 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) return z*X+x+1 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) - v0 = v01*xf + v00*(1-xf) - v1 = v11*xf + v10*(1-xf) + local v0 = v01*xf + v00*(1-xf) + local v1 = v11*xf + v10*(1-xf) return v1*zf + v0*(1-zf) end local data = {} -local blocksize = 6 +local blocksize = 20 local sea_level = 1 local min_catchment = 25 @@ -82,15 +101,62 @@ local function generate(minp, maxp, seed) for z = minp.z, maxp.z do local xb = x/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 = math.floor(xb) + local x0, z0 + 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 z0 = math.floor(zb) local z1 = z0+1 - - local xf = xb - x0 - local zf = zb - z0 + local xf, zf + do + 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 i01 = index(x1,z0) @@ -115,7 +181,7 @@ local function generate(minp, maxp, seed) local is_lake = lake_height > terrain_height local is_river = false - if xf == 0 then + if xf < 1/6 then if links[i00] == 1 and rivers[i00] >= min_catchment then is_river = true elseif links[i10] == 3 and rivers[i10] >= min_catchment then @@ -123,7 +189,7 @@ local function generate(minp, maxp, seed) end end - if zf == 0 then + if zf < 1/6 then if links[i00] == 2 and rivers[i00] >= min_catchment then is_river = true elseif links[i01] == 4 and rivers[i01] >= min_catchment then diff --git a/rivermapper.py b/rivermapper.py index ab35d55..b494748 100644 --- a/rivermapper.py +++ b/rivermapper.py @@ -18,7 +18,7 @@ neighbours_dirs = np.array([ neighbours_pattern = neighbours_dirs > 0 -def flow_dirs_lakes(dem, random=0.0625): +def flow_dirs_lakes(dem, random=0): (Y, X) = dem.shape dem_margin = np.zeros((Y+2, X+2)) diff --git a/terrain_rivers.py b/terrain_rivers.py index 3487658..2798c7d 100755 --- a/terrain_rivers.py +++ b/terrain_rivers.py @@ -4,6 +4,7 @@ import numpy as np import noise from save import save from erosion import EvolutionModel +import bounds import os import sys @@ -67,10 +68,18 @@ model.calculate_flow() 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.lakes, 'lakes', dtype='>i2') save(model.dirs, 'links', dtype='u1') 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: sfile.write('{:d}\n{:d}'.format(mapsize, mapsize))