This should help to de-clutter the main init file. If cubic noise is added, then these functions are essential, which is why they are not removed
1108 lines
26 KiB
Lua
1108 lines
26 KiB
Lua
vcnlib = {}
|
|
vcnlib.layers = {}
|
|
|
|
--Layer def
|
|
-- name
|
|
-- string
|
|
-- dimensions
|
|
-- 2 or 3
|
|
-- block_size
|
|
-- vector - norm 5^3 or nil
|
|
-- sector_lengths
|
|
-- vector - norm 300^3 or 2000^3
|
|
-- scale
|
|
-- interger - the sector lengths are multiplied by this, but the
|
|
-- noise produced has a lower resolution
|
|
-- biome_types
|
|
-- string - random,heatmap,heatmap_tol
|
|
-- biome_type_options
|
|
-- table - tollerances for heatmap
|
|
-- geometery
|
|
-- string - euclidean,manhattan,chebyshev
|
|
--
|
|
--Layer in mem
|
|
-- cache
|
|
-- table of tables
|
|
-- add_biome
|
|
-- function to add biomes to the layer
|
|
--
|
|
--
|
|
--Point in mem
|
|
-- pos
|
|
-- vector
|
|
-- biome
|
|
-- biome def table
|
|
|
|
|
|
|
|
--Returns the biome of the closest point from a table
|
|
--Must ensure that points cover the Moore environment of the sector
|
|
|
|
--[[
|
|
--TODO list
|
|
--Optimisation re-write of entire code base - DONE
|
|
--Docs
|
|
--loop flattening (optimisation)
|
|
--Bring your own table - DONE
|
|
--add more types of noise - cubic cell noise especially
|
|
--]]
|
|
|
|
--functions defined in local scope for performance
|
|
local minetest = minetest
|
|
local abs = math.abs
|
|
local floor = math.floor
|
|
local hash_pos = minetest.hash_node_position
|
|
|
|
--normal vector.add has a check for b not being a table, I don't need this
|
|
local vector_add = function(a,b)
|
|
return {x=a.x+b.x,y=a.y+b.y,z=a.z+b.z}
|
|
end
|
|
|
|
--this could be stored in the layer - possibly tracked at biome addition
|
|
local get_biome_num = function(layer)
|
|
return table.getn(layer.biomes)
|
|
end
|
|
|
|
|
|
--sector 0,0,0 has a smallest point at 0,0,0
|
|
local sector_to_pos = function(sector,layer)
|
|
local lengths = layer.sector_lengths
|
|
local pos = {}
|
|
if layer.dimensions == 3 then
|
|
pos.x = lengths.x * sector.x
|
|
pos.y = lengths.y * sector.y
|
|
pos.z = lengths.z * sector.z
|
|
else
|
|
pos.x = lengths.x * sector.x
|
|
pos.y = 0
|
|
pos.z = lengths.z * sector.z
|
|
end
|
|
return pos
|
|
end
|
|
|
|
--add function to api
|
|
vcnlib.sector_to_pos = sector_to_pos
|
|
|
|
--point 0,0,0 is in sector 0,0,0
|
|
local pos_to_sector = function(pos,layer)
|
|
local lengths = layer.sector_lengths
|
|
local sector = {x=pos.x,y=pos.y,z=pos.z}
|
|
if layer.dimensions == 3 then
|
|
sector.x = floor(sector.x/lengths.x)
|
|
sector.y = floor(sector.y/lengths.y)
|
|
sector.z = floor(sector.z/lengths.z)
|
|
else
|
|
sector.x = floor(sector.x/lengths.x)
|
|
sector.y = 0
|
|
sector.z = floor(sector.z/lengths.z)
|
|
end
|
|
return sector
|
|
end
|
|
|
|
vcnlib.pos_to_sector = pos_to_sector
|
|
|
|
--This is hot code, so checks are kept out of the looping sections
|
|
--so there is a lot of code duplication
|
|
local find_closest = function(pos,geo,dims,points)
|
|
local dist = nil
|
|
local mini = math.huge
|
|
local biome = nil
|
|
if geo == "manhattan" then
|
|
if dims == 3 then
|
|
for i=1,#points do
|
|
local v = points[i]
|
|
local x=abs(pos.x-v.pos.x)
|
|
local y=abs(pos.y-v.pos.y)
|
|
local z=abs(pos.z-v.pos.z)
|
|
dist = x+y+z
|
|
if dist < mini then
|
|
mini = dist
|
|
biome = v.biome
|
|
end
|
|
end
|
|
else
|
|
for i=1,#points do
|
|
local v = points[i]
|
|
local x=abs(pos.x-v.pos.x)
|
|
local z=abs(pos.z-v.pos.z)
|
|
dist = x+z
|
|
if dist < mini then
|
|
mini = dist
|
|
biome = v.biome
|
|
end
|
|
end
|
|
end
|
|
elseif geo == "chebyshev" then
|
|
if dims == 3 then
|
|
for i=1,#points do
|
|
local v = points[i]
|
|
local x=abs(pos.x-v.pos.x)
|
|
local y=abs(pos.y-v.pos.y)
|
|
local z=abs(pos.z-v.pos.z)
|
|
dist = greatest(x,y,z)
|
|
if dist < mini then
|
|
mini = dist
|
|
biome = v.biome
|
|
end
|
|
end
|
|
else
|
|
for i=1,#points do
|
|
local v = points[i]
|
|
local x=abs(pos.x-v.pos.x)
|
|
local x=abs(pos.x-v.pos.x)
|
|
local z=abs(pos.z-v.pos.z)
|
|
dist = greatest(x,0,z)
|
|
if dist < mini then
|
|
mini = dist
|
|
biome = v.biome
|
|
end
|
|
end
|
|
end
|
|
elseif geo =="euclidean" then
|
|
if dims == 2 then
|
|
for i=1,#points do
|
|
local v = points[i]
|
|
local x=abs(pos.x-v.pos.x)
|
|
local z=abs(pos.z-v.pos.z)
|
|
dist = (x*x)+(z*z)
|
|
if dist < mini then
|
|
mini = dist
|
|
biome = v.biome
|
|
end
|
|
end
|
|
else
|
|
for i=1,#points do
|
|
local v = points[i]
|
|
local x=abs(pos.x-v.pos.x)
|
|
local y=abs(pos.y-v.pos.y)
|
|
local z=abs(pos.z-v.pos.z)
|
|
dist = (x*x)+(y*y)+(z*z)
|
|
if dist < mini then
|
|
mini = dist
|
|
biome = v.biome
|
|
end
|
|
end
|
|
end
|
|
elseif geo =="oddprod" then
|
|
if dims == 2 then
|
|
for i=1,#points do
|
|
local v = points[i]
|
|
local x=abs(pos.x-v.pos.x)
|
|
local z=abs(pos.z-v.pos.z)
|
|
if x <= 1 then
|
|
x=1
|
|
end
|
|
if z <= 1 then
|
|
z=1
|
|
end
|
|
dist = abs(x*z)
|
|
if dist < mini then
|
|
mini = dist
|
|
biome = v.biome
|
|
end
|
|
end
|
|
else
|
|
for i=1,#points do
|
|
local v = points[i]
|
|
local x=abs(pos.x-v.pos.x)
|
|
local y=abs(pos.y-v.pos.y)
|
|
local z=abs(pos.z-v.pos.z)
|
|
if x <= 1 then
|
|
x=1
|
|
end
|
|
if y <= 1 then
|
|
y=1
|
|
end
|
|
if z <= 1 then
|
|
z=1
|
|
end
|
|
dist = abs(x*y*z)
|
|
if dist < mini then
|
|
mini = dist
|
|
biome = v.biome
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
return biome
|
|
end
|
|
|
|
--block locations must start at (0,0,0)
|
|
--for 2d use (x,y) rather than (x,0,z)
|
|
local blockfiller = function(blockdata,blocksize,table,tablesize,blockstart)
|
|
local tableit = blockstart
|
|
local ybuf,zbuf = tablesize.x - blocksize.x,(tablesize.y - blocksize.y)*tablesize.x
|
|
local x,y,z = 1,1,1
|
|
local blocklength = blocksize.x*blocksize.y*(blocksize.z or 1)
|
|
for i=1,blocklength do
|
|
if x > blocksize.x then
|
|
x = 1
|
|
y = y + 1
|
|
tableit = tableit + ybuf
|
|
end
|
|
if y > blocksize.y then
|
|
y = 1
|
|
z = z + 1
|
|
tableit = tableit + zbuf
|
|
end
|
|
table[tableit] = blockdata[i]
|
|
tableit = tableit + 1
|
|
x = x + 1
|
|
end
|
|
end
|
|
|
|
--block locations must start at (0,0,0)
|
|
local blockfiller_2d = function(blockdata,blocksize,table,tablesize,blockstart)
|
|
local tableit = blockstart
|
|
local zbuf = tablesize.x - blocksize.x
|
|
local x,z = 1,1
|
|
local blocklength = blocksize.x*blocksize.z
|
|
for i=1,blocklength do
|
|
if x > blocksize.x then
|
|
x = 1
|
|
z = z + 1
|
|
tableit = tableit + zbuf
|
|
end
|
|
table[tableit] = blockdata[i]
|
|
tableit = tableit + 1
|
|
x = x + 1
|
|
end
|
|
end
|
|
|
|
--Copy of the code in find_closest
|
|
--This should be used in any non-critical code
|
|
local get_dist = function(a,b,geo,dims)
|
|
if geo == "manhattan" then
|
|
if dims == 3 then
|
|
local x=abs(a.x-b.x)
|
|
local y=abs(a.y-b.y)
|
|
local z=abs(a.z-b.z)
|
|
return x+y+z
|
|
else
|
|
local x=abs(a.x-b.x)
|
|
local z=abs(a.z-b.z)
|
|
return x+z
|
|
end
|
|
elseif geo == "chebyshev" then
|
|
if dims == 3 then
|
|
local x=abs(a.x-b.x)
|
|
local y=abs(a.y-b.y)
|
|
local z=abs(a.z-b.z)
|
|
return greatest(x,y,z)
|
|
else
|
|
local x=abs(a.x-b.x)
|
|
local z=abs(a.z-b.z)
|
|
return greatest(x,0,z)
|
|
end
|
|
elseif geo =="euclidean" then
|
|
if dims == 3 then
|
|
local x=abs(a.x-b.x)
|
|
local y=abs(a.y-b.y)
|
|
local z=abs(a.z-b.z)
|
|
return math.sqrt((x*x)+(y*y)+(z*z))
|
|
else
|
|
local x=abs(a.x-b.x)
|
|
local z=abs(a.z-b.z)
|
|
return math.sqrt((x*x)+(z*z))
|
|
end
|
|
elseif geo =="oddprod" then
|
|
if dims == 2 then
|
|
local x=abs(a.x-b.x)
|
|
local z=abs(a.z-b.z)
|
|
if x <= 1 then
|
|
x=1
|
|
end
|
|
if z <= 1 then
|
|
z=1
|
|
end
|
|
return abs(x*z)
|
|
else
|
|
local x=abs(a.x-b.x)
|
|
local y=abs(a.y-b.y)
|
|
local z=abs(a.z-b.z)
|
|
if x <= 1 then
|
|
x=1
|
|
end
|
|
if y <= 1 then
|
|
y=1
|
|
end
|
|
if z <= 1 then
|
|
z=1
|
|
end
|
|
return abs(x*y*z)
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
--Uses PcgRandom for better range - a 32 bit random would limit sector sizes to
|
|
-- 600^3 due to randomness issues
|
|
local generate_points = function(sector,seed,layer)
|
|
local hash = hash_pos(sector)
|
|
local offset = layer.seed_offset
|
|
local prand = PcgRandom(hash + (seed + offset) % 100000)
|
|
local points = {}
|
|
local dims = layer.dimensions
|
|
local dist = layer.point_distribution
|
|
local seen = {}
|
|
--Distribution is completely user defined
|
|
local num = prand:next(dist.random_min,dist.random_max)
|
|
local set = false
|
|
for i=#dist,1,-1 do
|
|
if num <= dist[i] then
|
|
num = i
|
|
set = true
|
|
break
|
|
|
|
end
|
|
end
|
|
|
|
if not set then
|
|
num = 1
|
|
end
|
|
|
|
while num > 0 do
|
|
--The points are aligned to 0.1 of a block
|
|
--This used to be to 1 block, but having multiple points at
|
|
--the same distance was causing artifacts with the experimental gen
|
|
local x = prand:next(0,(layer.sector_lengths.x-1)*10)
|
|
local y
|
|
if dims == 3 then
|
|
y = prand:next(0,(layer.sector_lengths.y-1)*10)
|
|
else
|
|
y = 0
|
|
end
|
|
local z = prand:next(0,(layer.sector_lengths.z-1)*10)
|
|
local pos = {x=x/10,y=y/10,z=z/10}
|
|
local hashed = hash_pos(pos)
|
|
if not seen[hashed] then
|
|
pos = vector_add(pos,sector_to_pos(sector,layer))
|
|
table.insert(points,pos)
|
|
seen[hashed] = pos
|
|
end
|
|
num = num - 1
|
|
end
|
|
--The random number generator is returned for use in adding other
|
|
--properties to the points - biomes
|
|
return points , prand
|
|
end
|
|
|
|
--This is a wrapper around generate_points - this adds biomes and doesn't return the random
|
|
--number generator
|
|
local generate_biomed_points = function(sector,seed,layer,biome_meth)
|
|
local hash = hash_pos(sector)
|
|
--This is a cache for storing points that were already generated
|
|
--this should improve performance - but profiling breaks it
|
|
if layer.cache[hash] then
|
|
return layer.cache[hash]
|
|
end
|
|
local points,prand = generate_points(sector,seed,layer)
|
|
local biome_meth = biome_meth or layer.biome_types
|
|
local ret = {}
|
|
if biome_meth == "random" then
|
|
for i,v in ipairs(points) do
|
|
local num = prand:next(1,get_biome_num(layer))
|
|
table.insert(ret,{
|
|
pos = v,
|
|
biome = layer.biomes[num],
|
|
})
|
|
end
|
|
elseif biome_meth == "heatmap" then
|
|
local mapdims = layer.biome_maps.dimensions
|
|
for i,v in ipairs(points) do
|
|
local heat,humidity
|
|
if mapdims == 3 then
|
|
heat = layer.heat:get3d(v)
|
|
humidity = layer.humidity:get3d(v)
|
|
else
|
|
heat = layer.heat:get2d({x=v.x,y=v.z})
|
|
humidity = layer.humidity:get2d({x=v.x,y=v.z})
|
|
end
|
|
local dist = math.huge
|
|
local biome = nil
|
|
for j,k in ipairs(layer.biome_defs) do
|
|
local hot = heat - k.heat
|
|
local wet = humidity - k.humidity
|
|
local d = abs(hot) + abs(wet)
|
|
if d < dist then
|
|
biome = k.name
|
|
dist = d
|
|
end
|
|
end
|
|
table.insert(ret,{
|
|
pos = v,
|
|
biome = biome,
|
|
})
|
|
end
|
|
elseif biome_meth == "tolmap" then
|
|
local mapdims = layer.mapdims
|
|
local heattol = layer.tollerance.heat
|
|
local wettol = layer.tollerance.humidity
|
|
for i,v in ipairs(points) do
|
|
local heat,humidity
|
|
if mapdims == 3 then
|
|
heat = layer.heat:get3d(v)
|
|
humidity = layer.humidity:get3d(v)
|
|
else
|
|
heat = layer.heat:get2d({x=v.x,y=v.z})
|
|
humidity = layer.humidity:get2d({x=v.x,y=v.z})
|
|
end
|
|
local biomes = {}
|
|
local biome = nil
|
|
for j,k in ipairs(layer.biome_defs) do
|
|
local hot = abs(heat - k.heat)
|
|
local wet = abs(humidity - k.humidity)
|
|
if hot < heattol and wet < wettol then
|
|
table.insert(biomes,k)
|
|
end
|
|
end
|
|
local bionum = table.getn(biomes)
|
|
if bionum == 0 then
|
|
local dist = math.huge
|
|
local nbiome = nil
|
|
for j,k in ipairs(layer.biome_defs) do
|
|
local hot = heat - k.heat
|
|
local wet = humidity - k.humidity
|
|
local d = abs(hot) + abs(wet)
|
|
if d < dist then
|
|
nbiome = k.name
|
|
dist = d
|
|
end
|
|
end
|
|
table.insert(ret,{
|
|
pos = v,
|
|
biome = nbiome,
|
|
})
|
|
else
|
|
biome = biomes[prand:next(1,bionum)].name
|
|
table.insert(ret,{
|
|
pos = v,
|
|
biome = biome,
|
|
})
|
|
end
|
|
end
|
|
elseif biome_meth == "multimap" then
|
|
local mapdims = layer.mapdims
|
|
local tol = layer.tollerance
|
|
for i,v in ipairs(points) do
|
|
local maps = {}
|
|
if mapdims == 3 then
|
|
for j,k in ipairs(layer.biome_maps) do
|
|
maps[j] = k:get3d(v)
|
|
end
|
|
else
|
|
local pos = {x=v.x,y=v.z}
|
|
for j,k in ipairs(layer.biome_maps) do
|
|
maps[j] = k:get2d(pos)
|
|
end
|
|
end
|
|
local dist = math.huge
|
|
local nbiome = nil
|
|
for j,k in ipairs(layer.biome_defs) do
|
|
local d = 0
|
|
for l,m in ipairs(maps) do
|
|
d = d + abs(k[l] - m)
|
|
end
|
|
if d < dist then
|
|
nbiome = k.name
|
|
dist = d
|
|
end
|
|
end
|
|
table.insert(ret,{
|
|
pos = v,
|
|
biome = nbiome,
|
|
})
|
|
end
|
|
elseif biome_meth == "multitolmap" then
|
|
local mapdims = layer.mapdims
|
|
local tol = layer.tollerance
|
|
for i,v in ipairs(points) do
|
|
local maps = {}
|
|
if mapdims == 3 then
|
|
for j,k in ipairs(layer.biome_maps) do
|
|
maps[j] = k:get3d(v,layer)
|
|
end
|
|
else
|
|
local pos = {x=v.x,y=v.z}
|
|
for j,k in ipairs(layer.biome_maps) do
|
|
maps[j] = k:get2d(pos,layer)
|
|
end
|
|
end
|
|
local biomes = {}
|
|
local biome = nil
|
|
for j,k in ipairs(layer.biome_defs) do
|
|
local add = true
|
|
for l,m in ipairs(maps) do
|
|
local comp = abs(k[l] - m)
|
|
if comp > tol[l] then
|
|
add = false
|
|
break
|
|
end
|
|
|
|
end
|
|
if add then
|
|
table.insert(biomes,k)
|
|
end
|
|
end
|
|
local bionum = table.getn(biomes)
|
|
if bionum == 0 then
|
|
local dist = math.huge
|
|
local nbiome = nil
|
|
for j,k in ipairs(layer.biome_defs) do
|
|
local d = 0
|
|
for l,m in ipairs(maps) do
|
|
d = d + abs(k[l] - m)
|
|
end
|
|
if d < dist then
|
|
nbiome = k.name
|
|
dist = d
|
|
end
|
|
end
|
|
table.insert(ret,{
|
|
pos = v,
|
|
biome = nbiome,
|
|
})
|
|
else
|
|
biome = biomes[prand:next(1,bionum)].name
|
|
table.insert(ret,{
|
|
pos = v,
|
|
biome = biome,
|
|
})
|
|
end
|
|
end
|
|
end
|
|
layer.cache[hash] = ret
|
|
return ret
|
|
end
|
|
|
|
|
|
local generate_block = function(blocksize,blockcentre,blockmin,layer,seed,byot)
|
|
local points = {}
|
|
local block = byot or {}
|
|
local index = 1
|
|
local dims = layer.dimensions
|
|
local geo = layer.geometry
|
|
local blockmax = {x=blockmin.x+(blocksize.x-1),y=blockmin.y+(blocksize.y -1)
|
|
,z=blockmin.z+(blocksize.z-1)}
|
|
local sector = pos_to_sector(blockcentre,layer)
|
|
if dims == 3 then
|
|
local x,y,z = -1,-1,-1
|
|
for i=1,27 do
|
|
x = x + 1
|
|
if x > 1 then
|
|
x = -1
|
|
y = y + 1
|
|
end
|
|
if y > 1 then
|
|
y = -1
|
|
z = z + 1
|
|
end
|
|
local temp = generate_biomed_points(vector_add(sector,{x=x,y=y,z=z})
|
|
,seed,layer)
|
|
for i,v in ipairs(temp) do
|
|
points[index] = v
|
|
v.dist = get_dist(blockcentre,v.pos,layer.geometry,dims)
|
|
index = index + 1
|
|
end
|
|
end
|
|
else
|
|
local x,z = -1,-1
|
|
for i=1,9 do
|
|
x = x + 1
|
|
if x > 1 then
|
|
x = -1
|
|
z = z + 1
|
|
end
|
|
local temp = generate_biomed_points(vector_add(sector,{x=x,y=0,z=z})
|
|
,seed,layer)
|
|
for i,v in ipairs(temp) do
|
|
points[index] = v
|
|
v.dist = get_dist(blockcentre,v.pos,layer.geometry,dims)
|
|
index = index + 1
|
|
end
|
|
end
|
|
end
|
|
table.sort(points,function(a,b) return a.dist < b.dist end)
|
|
local to_nil = false
|
|
local max_dist = points[1].dist + get_dist(blockmin,blockcentre,geo,dims)
|
|
for i=1,#points do
|
|
if to_nil then
|
|
points[i] = nil
|
|
elseif points[i].dist > max_dist then
|
|
to_nil = true
|
|
end
|
|
end
|
|
if #points == 1 then
|
|
if dims == 3 then
|
|
local tablesize = blocksize.x*blocksize.y*blocksize.z
|
|
local x,y,z = blockmin.x,blockmin.y,blockmin.z
|
|
local biome = point[1].biome
|
|
for i = 1,tablesize do
|
|
if x > blockmax.x then
|
|
x = blockmin.x
|
|
y = y + 1
|
|
end
|
|
if y > blockmax.y then
|
|
y = blockmin.y
|
|
z = z + 1
|
|
end
|
|
block[i] = biome
|
|
x = x + 1
|
|
end
|
|
else
|
|
local tablesize = blocksize.x*blocksize.z
|
|
local x,y = blockmin.x,blockmin.z
|
|
local biome = point[1].biome
|
|
for i = 1,tablesize do
|
|
if x> blockmax.x then
|
|
x = blockmin.x
|
|
y = y + 1
|
|
end
|
|
block[i] = biome
|
|
x = x + 1
|
|
end
|
|
end
|
|
elseif dims == 3 then
|
|
local tablesize = blocksize.x*blocksize.y*blocksize.z
|
|
local x,y,z = blockmin.x,blockmin.y,blockmin.z
|
|
for i = 1,tablesize do
|
|
if x > blockmax.x then
|
|
x = blockmin.x
|
|
y = y + 1
|
|
end
|
|
if y > blockmax.y then
|
|
y = blockmin.y
|
|
z = z + 1
|
|
end
|
|
block[i] = find_closest({x=x,y=y,z=z},geo
|
|
,dims,points)
|
|
x = x + 1
|
|
end
|
|
else
|
|
local tablesize = blocksize.x*blocksize.z
|
|
local x,y = blockmin.x,blockmin.z
|
|
for i = 1,tablesize do
|
|
if x> blockmax.x then
|
|
x = blockmin.x
|
|
y = y + 1
|
|
end
|
|
block[i] = find_closest({x=x,y=y,z=z},geo
|
|
,dims,points)
|
|
x = x + 1
|
|
end
|
|
end
|
|
return block
|
|
end
|
|
|
|
local shared_block_byot = {}
|
|
|
|
--map is generated in blocks
|
|
--this allows for distance testing to reduce the number of points to test
|
|
local get_biome_map_3d_experimental = function(minp,maxp,layer,seed,byot)
|
|
--normal block size
|
|
local blsize = layer.blocksize or {x=5,y=5,z=5}
|
|
local halfsize = {x=blsize.x/2,y=blsize.y/2,z=blsize.z/2}
|
|
local centre = {x=minp.x+halfsize.x,y=minp.y+halfsize.y,z=minp.z+halfsize.z}
|
|
--the size of this block
|
|
local blocksize = {x=blsize.x,y=blsize.y,z=blsize.z}
|
|
local blockmin = {x=minp.x,y=minp.y,z=minp.z}
|
|
local mapsize = {x=maxp.x-minp.x+1,y=maxp.y-minp.y+1,z=maxp.z-minp.z+1}
|
|
--bring your own table - reduce garbage collections
|
|
local map = byot or {}
|
|
local block_byot = nil
|
|
if byot then
|
|
block_byot = shared_block_byot
|
|
end
|
|
|
|
for z=minp.z,maxp.z,blsize.z do
|
|
centre.z = z + halfsize.z
|
|
blockmin.z = z
|
|
if z + (blsize.z - 1) > maxp.z then
|
|
blocksize.z = blsize.z - ((z + (blsize.z - 1)) - maxp.z)
|
|
centre.z = z + blocksize.z/2
|
|
end
|
|
for y=minp.y,maxp.y,blsize.y do
|
|
centre.y = y + halfsize.y
|
|
blockmin.y = y
|
|
if y + (blsize.y - 1) > maxp.y then
|
|
blocksize.y = blsize.y - ((y + (blsize.y - 1)) - maxp.y)
|
|
centre.y = y + blocksize.y/2
|
|
end
|
|
for x=minp.x,maxp.x,blsize.x do
|
|
centre.x = x + halfsize.x
|
|
blockmin.x = x
|
|
if x + (blsize.x - 1) > maxp.x then
|
|
blocksize.x = blsize.x - ((x + (blsize.x -1)) - maxp.x)
|
|
centre.x = x + blocksize.x/2
|
|
end
|
|
local temp = generate_block(blocksize,centre,blockmin
|
|
,layer,seed,block_byot)
|
|
local blockstart = blockmin.x - minp.x + 1
|
|
+ (blockmin.y - minp.y)*mapsize.x
|
|
+ (blockmin.z - minp.z)*mapsize.x*mapsize.y
|
|
blockfiller(temp,blocksize,map,mapsize,blockstart)
|
|
end
|
|
end
|
|
end
|
|
|
|
return map
|
|
end
|
|
|
|
vcnlib.experimental_3d = get_biome_map_3d_experimental
|
|
|
|
local get_biome_map_2d_experimental = function(minp,maxp,layer,seed,byot)
|
|
local blsize = layer.blocksize or {x=5,y=0,z=5}
|
|
local halfsize = {x=blsize.x/2,y=0,z=blsize.z/2}
|
|
local centre = {x=minp.x+halfsize.x,y=0,z=minp.z+halfsize.z}
|
|
local blocksize = {x=blsize.x,y=0,z=blsize.z}
|
|
local blockmin = {x=minp.x,y=0,z=minp.z}
|
|
local mapsize = {x=maxp.x-minp.x+1,y=0,z=maxp.z-minp.z+1}
|
|
local map = byot or {}
|
|
local block_byot
|
|
if byot then
|
|
block_byot = shared_block_byot
|
|
end
|
|
|
|
for z=minp.z,maxp.z,blsize.z do
|
|
centre.z = z + halfsize.z
|
|
blockmin.z = z
|
|
if z + (blsize.z - 1) > maxp.z then
|
|
blocksize.z = blsize.z - ((z + (blsize.z - 1)) - maxp.z)
|
|
centre.z = z + blocksize.z/2
|
|
end
|
|
for x=minp.x,maxp.x,blsize.x do
|
|
centre.x = x + halfsize.x
|
|
blockmin.x = x
|
|
if x + (blsize.x - 1) > maxp.x then
|
|
blocksize.x = blsize.x - ((x + (blsize.x -1)) - maxp.x)
|
|
centre.x = x + blocksize.x/2
|
|
end
|
|
local temp = generate_block(blocksize,centre,blockmin
|
|
,layer,seed,block_byot)
|
|
local blockstart = blockmin.x - minp.x + 1
|
|
+ (blockmin.z - minp.z)*mapsize.x
|
|
blockfiller_2d(temp,blocksize,map,mapsize,blockstart)
|
|
end
|
|
end
|
|
|
|
return map
|
|
end
|
|
|
|
vcnlib.experimental_2d = get_biome_map_2d_experimental
|
|
|
|
local greatest = function(x,y,z)
|
|
if x>y then
|
|
if x>z then
|
|
return x
|
|
else
|
|
return z
|
|
end
|
|
else
|
|
if y>z then
|
|
return y
|
|
else
|
|
return z
|
|
end
|
|
end
|
|
end
|
|
|
|
--simple test to find the biome of a node
|
|
--used as the basis of the simple map generation methods
|
|
local get_node_biome = function(pos,seed,layer)
|
|
local sector = pos_to_sector(pos,layer)
|
|
local dims = layer.dimensions
|
|
local points = {}
|
|
local x,y,z = -1,-1,-1
|
|
local index = 1
|
|
if dims == 3 then
|
|
for i=1,27 do
|
|
if x > 1 then
|
|
x = -1
|
|
y = y + 1
|
|
end
|
|
if y > 1 then
|
|
y = -1
|
|
z = z + 1
|
|
end
|
|
local temp = generate_biomed_points(vector_add(sector,{x=x,y=y,z=z})
|
|
,seed,layer)
|
|
for i,v in ipairs(temp) do
|
|
points[index] = v
|
|
index = index + 1
|
|
end
|
|
x = x + 1
|
|
end
|
|
else
|
|
for i=1,9 do
|
|
if x > 1 then
|
|
x = -1
|
|
z = z + 1
|
|
end
|
|
local temp = generate_biomed_points(vector_add(sector,{x=x,y=0,z=z})
|
|
,seed,layer)
|
|
for i,v in ipairs(temp) do
|
|
points[index] = v
|
|
index = index + 1
|
|
end
|
|
x = x + 1
|
|
end
|
|
end
|
|
return find_closest(pos,layer.geometry,dims,points)
|
|
end
|
|
|
|
vcnlib.get_node_biome = get_node_biome
|
|
|
|
--Simple biome map implimentation
|
|
--requires scaling to perform usably well
|
|
local get_biome_map_3d_flat = function(minp,maxp,layer,seed,byot)
|
|
local ret = byot or {}
|
|
local nixyz = 1
|
|
local table_size = ((maxp.z - minp.z) + 1)*((maxp.y - minp.y) + 1)
|
|
*((maxp.x - minp.x) + 1)
|
|
local x,y,z = minp.x,minp.y,minp.z
|
|
for nixyz=1,table_size do
|
|
if x > maxp.x then
|
|
x = minp.x
|
|
y = y + 1
|
|
end
|
|
if y > maxp.y then
|
|
y = minp.y
|
|
z = z + 1
|
|
end
|
|
ret[nixyz] = get_node_biome({x=x,y=y,z=z},seed,layer)
|
|
x = x + 1
|
|
end
|
|
|
|
return ret
|
|
end
|
|
|
|
vcnlib.get_biome_map_3d_simple = get_biome_map_3d_flat
|
|
|
|
--Simple 2d biome map implimentation
|
|
--Functions usably without scaling - have not tested against the experimental
|
|
--function, should be slower though
|
|
local get_biome_map_2d_flat = function(minp,maxp,layer,seed,byot)
|
|
local ret = byot or {}
|
|
local nixz = 1
|
|
|
|
for z=minp.z,maxp.z do
|
|
for x=minp.x,maxp.x do
|
|
ret[nixz] = get_node_biome({x=x,y=y,z=z},seed,layer)
|
|
nixz = nixz + 1
|
|
end
|
|
end
|
|
return ret
|
|
end
|
|
|
|
vcnlib.get_biome_map_2d_simple = get_biome_map_2d_flat
|
|
|
|
--This function can be used to scale any compliant 2d map generator
|
|
--This adds an extra overhead - but this is negligable
|
|
local scale_2d_map_flat = function(minp,maxp,layer,seed,map_gen,byot,scale_byot)
|
|
local minp,rmin = minp,minp
|
|
local maxp,rmax = maxp,maxp
|
|
if layer.scale then
|
|
minp = {x=floor(minp.x/scale),y=0,z=floor(minp.z/scale)}
|
|
maxp = {x=floor(maxp.x/scale),y=0,z=floor(maxp.z/scale)}
|
|
end
|
|
|
|
local ret
|
|
if layer.scale then
|
|
ret = scale_byot or {}
|
|
else
|
|
ret = byot or {}
|
|
end
|
|
|
|
ret = map_gen(minp,maxp,layer,seed,map_gen,ret)
|
|
|
|
if layer.scale then
|
|
local nixz = 1
|
|
local scalxz = 1
|
|
local scalsidx = abs(maxp.x - minp.x) + 1
|
|
local sx,sz,ix = 0,0,1
|
|
local newret = byot or {}
|
|
for z=rmin.z,rmax.z do
|
|
sx = 0
|
|
for x=rmin.x,rmax.x do
|
|
newret[nixz] = ret[scalxz]
|
|
nixz = nixz + 1
|
|
sx = sx + 1
|
|
if sx == scale then
|
|
scalxz = scalxz + 1
|
|
sx = 0
|
|
end
|
|
end
|
|
sz = sz + 1
|
|
if sz ~= scale then
|
|
scalxz = ix
|
|
else
|
|
scalxz = ix + scalsidx
|
|
ix = scalxz
|
|
sz = 0
|
|
end
|
|
end
|
|
ret = newret
|
|
end
|
|
end
|
|
|
|
--This function can be used to scale any compliant 3d map generator
|
|
--This adds an extra overhead - but this is negligable
|
|
local scale_3d_map_flat = function(minp,maxp,layer,seed,map_gen,byot,scale_byot)
|
|
local scale = layer.scale
|
|
local minp,rmin = minp,minp
|
|
local maxp,rmax = maxp,maxp
|
|
if layer.scale then
|
|
minp = {x=floor(minp.x/scale),y=floor(minp.y/scale)
|
|
,z=floor(minp.z/scale)}
|
|
--Replace def_table with map object
|
|
maxp = {x=floor(maxp.x/scale),y=floor(maxp.y/scale)
|
|
,z=floor(maxp.z/scale)}
|
|
end
|
|
|
|
local ret
|
|
if layer.scale then
|
|
ret = scale_byot or {}
|
|
else
|
|
ret = byot or {}
|
|
end
|
|
|
|
ret = map_gen(minp,maxp,layer,seed,ret)
|
|
|
|
if scale then
|
|
local nixyz = 1
|
|
local scalxyz = 1
|
|
local scalsidx = abs(maxp.x - minp.x) + 1
|
|
local scalsidy = abs(maxp.y - minp.y) + 1
|
|
local sx,sy,sz,ix,iy = 0,0,0,1,1
|
|
local table_size = ((rmax.z - rmin.z) + 1)*((rmax.y - rmin.y) + 1)
|
|
*((rmax.x - rmin.x) + 1)
|
|
local x,y,z = rmin.x,rmin.y,rmin.z
|
|
local newret = byot or {}
|
|
for z=rmin.z,rmax.z do
|
|
sy = 0
|
|
for y=rmin.y,rmax.y do
|
|
sx = 0
|
|
for x=rmin.x,rmax.x do
|
|
newret[nixyz] = ret[scalxyz]
|
|
nixyz = nixyz + 1
|
|
sx = sx + 1
|
|
if sx == scale then
|
|
scalxyz = scalxyz + 1
|
|
sx = 0
|
|
end
|
|
end
|
|
sy = sy + 1
|
|
if sy ~= scale then
|
|
scalxyz = ix
|
|
else
|
|
scalxyz = ix + scalsidx
|
|
ix = scalxyz
|
|
sy = 0
|
|
end
|
|
end
|
|
sz = sz + 1
|
|
if sz ~= scale then
|
|
scalxyz = iy
|
|
ix = iy
|
|
else
|
|
sz = 0
|
|
scalxyz = iy + scalsidy*scalsidx
|
|
iy = scalxyz
|
|
ix = iy
|
|
end
|
|
end
|
|
ret = newret
|
|
end
|
|
return ret
|
|
end
|
|
|
|
local shared_scale_byot = {}
|
|
|
|
--This is a single function which can be called to produce a biomemap
|
|
--for any layer type
|
|
--Attempts to choose the most optimal type for a given layer
|
|
--All scale code is condtional, so is safe to add to any mapgen
|
|
vcnlib.get_biome_map_flat = function(minp,maxp,layer,seed,byot)
|
|
local scale_byot = nil
|
|
if byot then
|
|
scale_byot = shared_scale_byot
|
|
end
|
|
|
|
if layer.dimensions == 3 then
|
|
local map_gen = nil
|
|
if layer.blocksize then
|
|
map_gen = get_biome_map_3d_experimental
|
|
else
|
|
map_gen = get_biome_map_3d_flat
|
|
end
|
|
|
|
return scale_3d_map_flat(minp,maxp,layer,seed,map_gen,byot,scale_byot)
|
|
else
|
|
local map_gen = nil
|
|
if layer.blocksize then
|
|
map_gen = get_biome_map_2d_experimental
|
|
else
|
|
map_gen = get_biome_map_2d_flat
|
|
end
|
|
|
|
return scale_2d_map_flat(minp,maxp,layer,seed,map_gen,byot,scale_byot)
|
|
end
|
|
end
|
|
|
|
vcnlib.new_layer = function(def)
|
|
local name = def.name
|
|
if vcnlib.layers[name] then
|
|
return
|
|
end
|
|
vcnlib.layers[name] = def
|
|
local layer = vcnlib.layers[name]
|
|
if not layer.seed_offset then
|
|
layer.seed_offset = 0
|
|
end
|
|
layer.biomes = {}
|
|
layer.biome_defs ={}
|
|
layer.add_biome = function(self,biome_def)
|
|
table.insert(self.biomes,biome_def.name)
|
|
table.insert(self.biome_defs,biome_def)
|
|
end
|
|
layer.get_biome_list = function(self,to_get)
|
|
return self.biomes
|
|
end
|
|
--this doesn't respect the world seed
|
|
if layer.biome_types == "heatmap"
|
|
or layer.biome_types == "tolmap" then
|
|
layer.heat = PerlinNoise(layer.biome_maps.heat)
|
|
layer.humidity = PerlinNoise(layer.biome_maps.humidity)
|
|
end
|
|
layer.cache = setmetatable({},vcnlib.meta_cache)
|
|
return layer
|
|
end
|
|
|
|
--for mods which are using a pre-defined biome layer
|
|
vcnlib.get_layer = function(to_get)
|
|
return vcnlib.layers[to_get]
|
|
end
|
|
|
|
vcnlib.meta_cache = {
|
|
__mode = "v",
|
|
}
|
|
|
|
--This code is used to test for custom maps - any table without get3d is
|
|
--assumed a def table for minetest.get_perlin
|
|
minetest.register_on_mapgen_init(function(map)
|
|
for k,layer in pairs(vcnlib.layers) do
|
|
for map_key,def_table in ipairs(v.biome_maps) do
|
|
--Add layer offset to map seed offset
|
|
def_table.seed_offset = def_table.seed_offset + layer.seed_offset
|
|
--Replace def_table with map object
|
|
v.biome_maps[j] = minetest.get_perlin(l)
|
|
end
|
|
end
|
|
end)
|
|
|
|
--dofile(minetest.get_modpath("vcnlib").."/testtools.lua")
|
|
--dofile(minetest.get_modpath("vcnlib").."/test_layer.lua")
|
|
dofile(minetest.get_modpath("vcnlib").."/maps.lua")
|
|
--dofile(minetest.get_modpath("vcnlib").."/test_layer.lua")
|