2021-06-03 19:21:45 +02:00
local modpath = mapgen_rivers.modpath
2020-11-14 19:12:12 +01:00
local mod_data_path = modpath .. ' river_data/ '
2020-04-26 23:29:36 +02:00
if not io.open ( mod_data_path .. ' size ' , ' r ' ) then
mod_data_path = modpath .. ' demo_data/ '
end
2021-06-03 23:30:04 +02:00
local world_data_path = mapgen_rivers.world_data_path
2020-04-26 23:29:36 +02:00
minetest.mkdir ( world_data_path )
2021-06-03 20:08:57 +02:00
dofile ( modpath .. ' load.lua ' )
2020-04-14 21:11:54 +02:00
2021-06-05 11:24:28 +02:00
mapgen_rivers.grid = { }
local X = 1000
local Z = 1000
2021-06-26 11:50:52 +02:00
local function offset_converter ( o )
return ( o + 0.5 ) * ( 1 / 256 )
end
2021-06-26 13:13:41 +02:00
local load_all = mapgen_rivers.settings . load_all
2021-06-03 20:08:57 +02:00
-- Try to read file 'size'
local sfile = io.open ( world_data_path .. ' size ' , ' r ' )
2021-06-26 13:13:41 +02:00
local first_mapgen = true
2021-06-05 11:24:28 +02:00
if sfile then
X , Z = tonumber ( sfile : read ( ' *l ' ) ) , tonumber ( sfile : read ( ' *l ' ) )
sfile : close ( )
2021-06-26 13:13:41 +02:00
first_mapgen = false
end
2021-06-26 11:50:52 +02:00
2021-06-26 13:13:41 +02:00
if first_mapgen then
-- Generate a map!!
local pregenerate = dofile ( mapgen_rivers.modpath .. ' /pregenerate.lua ' )
2021-06-05 11:24:28 +02:00
minetest.register_on_mods_loaded ( function ( )
2021-06-26 13:13:41 +02:00
print ( ' Generating grid ' )
pregenerate ( load_all )
2021-06-05 11:24:28 +02:00
2021-06-26 13:13:41 +02:00
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
2021-06-05 11:24:28 +02:00
end )
2021-06-26 13:13:41 +02:00
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
2021-06-26 11:50:52 +02:00
minetest.register_on_mods_loaded ( function ( )
2021-06-26 13:13:41 +02:00
if load_all then
print ( ' Loading full grid ' )
else
print ( ' Loading grid as interactive loaders ' )
2021-06-26 11:50:52 +02:00
end
2021-06-26 13:13:41 +02:00
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 )
2021-06-26 11:50:52 +02:00
end )
2020-04-14 21:11:54 +02:00
end
2021-06-05 11:24:28 +02:00
mapgen_rivers.grid . size = { x = X , y = Z }
2020-04-14 21:11:54 +02:00
local function index ( x , z )
return z * X + x + 1
end
2021-06-06 13:25:43 +02:00
local blocksize = mapgen_rivers.settings . blocksize
local min_catchment = mapgen_rivers.settings . min_catchment
local max_catchment = mapgen_rivers.settings . max_catchment
2020-12-22 16:38:30 +01:00
local map_offset = { x = 0 , z = 0 }
2021-06-06 13:25:43 +02:00
if mapgen_rivers.settings . center then
2020-12-22 16:38:30 +01:00
map_offset.x = blocksize * X / 2
map_offset.z = blocksize * Z / 2
2020-12-20 22:28:54 +01:00
end
2020-04-14 21:11:54 +02:00
-- 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
2020-05-24 11:38:47 +02:00
local noise_heat -- Need a large-scale noise here so no heat blend
2021-06-06 13:25:43 +02:00
local elevation_chill = mapgen_rivers.settings . elevation_chill
2020-05-24 11:38:47 +02:00
local function get_temperature ( x , y , z )
local pos = { x = x , y = z }
return noise_heat : get2d ( pos ) - y * elevation_chill
end
2021-06-06 13:25:43 +02:00
local glaciers = mapgen_rivers.settings . glaciers
local glacier_factor = mapgen_rivers.settings . glacier_factor
2020-05-24 11:38:47 +02:00
local init = false
2020-04-26 17:13:38 +02:00
-- On map generation, determine into which polygon every point (in 2D) will fall.
-- Also store polygon-specific data
2020-04-14 21:11:54 +02:00
local function make_polygons ( minp , maxp )
2020-12-20 22:28:54 +01:00
2021-06-05 11:24:28 +02:00
local grid = mapgen_rivers.grid
local dem = grid.dem
local lakes = grid.lakes
local dirs = grid.dirs
local rivers = grid.rivers
local offset_x = grid.offset_x
local offset_z = grid.offset_y
2020-05-24 11:38:47 +02:00
if not init then
if glaciers then
noise_heat = minetest.get_perlin ( mapgen_rivers.noise_params . heat )
end
init = true
end
2020-07-21 12:46:23 +02:00
local chulens = maxp.x - minp.x + 1
2020-04-14 21:11:54 +02:00
local polygons = { }
2020-04-26 17:13:38 +02:00
-- Determine the minimum and maximum coordinates of the polygons that could be on the chunk, knowing that they have an average size of 'blocksize' and a maximal offset of 0.5 blocksize.
2020-12-22 16:38:30 +01:00
local xpmin , xpmax = math.max ( math.floor ( ( minp.x + map_offset.x ) / blocksize - 0.5 ) , 0 ) , math.min ( math.ceil ( ( maxp.x + map_offset.x ) / blocksize + 0.5 ) , X - 2 )
local zpmin , zpmax = math.max ( math.floor ( ( minp.z + map_offset.z ) / blocksize - 0.5 ) , 0 ) , math.min ( math.ceil ( ( maxp.z + map_offset.z ) / blocksize + 0.5 ) , Z - 2 )
2020-04-14 21:11:54 +02:00
2020-04-26 17:13:38 +02:00
-- Iterate over the polygons
2020-04-14 21:11:54 +02:00
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 )
2020-04-26 17:13:38 +02:00
-- Extract the vertices of the polygon
2020-12-22 16:38:30 +01:00
local poly_x = {
( offset_x [ iA ] + xp ) * blocksize - map_offset.x ,
( offset_x [ iB ] + xp + 1 ) * blocksize - map_offset.x ,
( offset_x [ iC ] + xp + 1 ) * blocksize - map_offset.x ,
( offset_x [ iD ] + xp ) * blocksize - map_offset.x ,
}
local poly_z = {
( offset_z [ iA ] + zp ) * blocksize - map_offset.z ,
( offset_z [ iB ] + zp ) * blocksize - map_offset.z ,
( offset_z [ iC ] + zp + 1 ) * blocksize - map_offset.z ,
( offset_z [ iD ] + zp + 1 ) * blocksize - map_offset.z ,
}
2020-04-14 21:11:54 +02:00
local polygon = { x = poly_x , z = poly_z , i = { iA , iB , iC , iD } }
2020-07-21 12:46:23 +02:00
local bounds = { } -- Will be a list of the intercepts of polygon edges for every Z position (scanline algorithm)
-- Calculate the min and max Z positions
2020-12-22 16:38:30 +01:00
local zmin = math.max ( math.floor ( math.min ( unpack ( poly_z ) ) ) + 1 , minp.z )
local zmax = math.min ( math.floor ( math.max ( unpack ( poly_z ) ) ) , maxp.z )
2020-04-26 17:13:38 +02:00
-- And initialize the arrays
2020-07-21 12:46:23 +02:00
for z = zmin , zmax do
bounds [ z ] = { }
2020-04-14 21:11:54 +02:00
end
local i1 = 4
for i2 = 1 , 4 do -- Loop on 4 edges
2020-07-21 12:46:23 +02:00
local z1 , z2 = poly_z [ i1 ] , poly_z [ i2 ]
-- Calculate the integer Z positions over which this edge spans
2020-12-22 16:38:30 +01:00
local lzmin = math.floor ( math.min ( z1 , z2 ) ) + 1
local lzmax = math.floor ( math.max ( z1 , z2 ) )
2020-07-21 12:46:23 +02:00
if lzmin <= lzmax then -- If there is at least one position in it
local x1 , x2 = poly_x [ i1 ] , poly_x [ i2 ]
-- Calculate coefficient of the equation defining the edge: X=aZ+b
local a = ( x1 - x2 ) / ( z1 - z2 )
2020-12-22 16:38:30 +01:00
local b = ( x1 - a * z1 )
2020-07-21 12:46:23 +02:00
for z = math.max ( lzmin , minp.z ) , math.min ( lzmax , maxp.z ) do
-- For every Z position involved, add the intercepted X position in the table
table.insert ( bounds [ z ] , a * z + b )
2020-04-14 21:11:54 +02:00
end
end
i1 = i2
end
2020-07-21 12:46:23 +02:00
for z = zmin , zmax do
2020-04-26 17:13:38 +02:00
-- Now sort the bounds list
2020-07-21 12:46:23 +02:00
local zlist = bounds [ z ]
table.sort ( zlist )
local c = math.floor ( # zlist / 2 )
2020-04-14 21:11:54 +02:00
for l = 1 , c do
2020-07-21 12:46:23 +02:00
-- Take pairs of X coordinates: all positions between them belong to the polygon.
local xmin = math.max ( math.floor ( zlist [ l * 2 - 1 ] ) + 1 , minp.x )
local xmax = math.min ( math.floor ( zlist [ l * 2 ] ) , maxp.x )
local i = ( z - minp.z ) * chulens + ( xmin - minp.x ) + 1
for x = xmin , xmax do
2020-04-26 17:13:38 +02:00
-- Fill the map at these places
2020-04-14 21:11:54 +02:00
polygons [ i ] = polygon
i = i + 1
end
end
end
2020-05-24 11:38:47 +02:00
local poly_dem = { dem [ iA ] , dem [ iB ] , dem [ iC ] , dem [ iD ] }
polygon.dem = poly_dem
2020-12-22 18:34:30 +01:00
polygon.lake = { lakes [ iA ] , lakes [ iB ] , lakes [ iC ] , lakes [ iD ] }
2020-04-14 21:11:54 +02:00
2020-04-26 17:13:38 +02:00
-- Now, rivers.
2020-04-26 18:10:23 +02:00
-- Load river flux values for the 4 corners
local riverA = river_width ( rivers [ iA ] )
local riverB = river_width ( rivers [ iB ] )
local riverC = river_width ( rivers [ iC ] )
local riverD = river_width ( rivers [ iD ] )
2020-05-24 11:38:47 +02:00
if glaciers then -- Widen the river
2020-12-22 16:38:30 +01:00
if get_temperature ( poly_x [ 1 ] , poly_dem [ 1 ] , poly_z [ 1 ] ) < 0 then
2020-05-24 11:38:47 +02:00
riverA = math.min ( riverA * glacier_factor , 1 )
end
2020-12-22 16:38:30 +01:00
if get_temperature ( poly_x [ 2 ] , poly_dem [ 2 ] , poly_z [ 2 ] ) < 0 then
2020-05-24 11:38:47 +02:00
riverB = math.min ( riverB * glacier_factor , 1 )
end
2020-12-22 16:38:30 +01:00
if get_temperature ( poly_x [ 3 ] , poly_dem [ 3 ] , poly_z [ 3 ] ) < 0 then
2020-05-24 11:38:47 +02:00
riverC = math.min ( riverC * glacier_factor , 1 )
end
2020-12-22 16:38:30 +01:00
if get_temperature ( poly_x [ 4 ] , poly_dem [ 4 ] , poly_z [ 4 ] ) < 0 then
2020-05-24 11:38:47 +02:00
riverD = math.min ( riverD * glacier_factor , 1 )
end
end
2020-04-27 21:08:15 +02:00
polygon.river_corners = { riverA , 1 - riverB , 2 - riverC , 1 - riverD }
2020-04-26 18:10:23 +02:00
-- Flow directions
local dirA , dirB , dirC , dirD = dirs [ iA ] , dirs [ iB ] , dirs [ iC ] , dirs [ iD ]
-- Determine the river flux on the edges, by testing dirs values
local river_west = ( dirA == 1 and riverA or 0 ) + ( dirD == 3 and riverD or 0 )
local river_north = ( dirA == 2 and riverA or 0 ) + ( dirB == 4 and riverB or 0 )
local river_east = 1 - ( dirB == 1 and riverB or 0 ) - ( dirC == 3 and riverC or 0 )
local river_south = 1 - ( dirD == 2 and riverD or 0 ) - ( dirC == 4 and riverC or 0 )
2020-04-14 21:11:54 +02:00
polygon.rivers = { river_west , river_north , river_east , river_south }
end
end
return polygons
end
return make_polygons