Compare commits
No commits in common. "7b29f68ffa41e6907b99e380f867b54ef66bf113" and "67f780b2b396856822c71533fcb2290f5a4c1192" have entirely different histories.
7b29f68ffa
...
67f780b2b3
918
init.lua
918
init.lua
|
@ -1,917 +1,21 @@
|
||||||
-- Local variables used for programming
|
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||||
local internal =
|
|
||||||
{ a = 0
|
|
||||||
|
|
||||||
-- Average size of a cave biome
|
local function load(name)
|
||||||
, biome_size = { x = 50, y = 50, z = 50 }
|
dofile(modpath.."/lua/"..name..".lua")
|
||||||
|
end
|
||||||
-- Average blob size of connectivity noise params
|
|
||||||
, cnct_blob = { x = 250, y = 100, z = 250 }
|
|
||||||
|
|
||||||
-- The margin that a voxelmanip gives around the chunk being generated
|
|
||||||
, mapgen_buffer = 16
|
|
||||||
|
|
||||||
-- Point that can be used for generating Voronoi graphs
|
|
||||||
-- The number can be anything, as long as it reasonably far away from
|
|
||||||
-- the range 0 - 100
|
|
||||||
, outlandish_point = -1e3
|
|
||||||
|
|
||||||
-- Random seeds used to influence noise
|
|
||||||
, seeds =
|
|
||||||
{ connectivity = 297948
|
|
||||||
, heat = 320523
|
|
||||||
, humidity = 9923473
|
|
||||||
, verticality = 35644
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Size of blocks being combined to simplify cave shape generation
|
|
||||||
-- Higher values means better performance, but lower detail and variations
|
|
||||||
, shape_cluster = { x = 32, y = 32, z = 32 }
|
|
||||||
|
|
||||||
-- Average size of a shape biome
|
|
||||||
, shape_size = { x = 50, y = 50, z = 50 }
|
|
||||||
|
|
||||||
-- Average blob size of verticality noise params
|
|
||||||
, vrtcl_blob = { x = 100, y = 250, z = 100 }
|
|
||||||
|
|
||||||
-- If another mod doesn't override this value, we will assume that this is
|
|
||||||
-- the world's depth.
|
|
||||||
, world_depth = -60
|
|
||||||
|
|
||||||
-- Lower bound for the entire world
|
|
||||||
, world_minp = { x = -1e6, y = -1e6, z = -1e6 }
|
|
||||||
|
|
||||||
-- Upper bound for cave generation
|
|
||||||
, world_maxp = { x = 1e6, y = 1e6, z = 1e6 }
|
|
||||||
}
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- Flat3dArray
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- The Flat3dArray helps translate, transform and read data from different
|
|
||||||
-- 1-dimensional flat tables that all represent some 3-dimensional set.
|
|
||||||
local Flat3dArray = {}
|
|
||||||
Flat3dArray.__index = Flat3dArray
|
|
||||||
|
|
||||||
-- Global variable that can be used by other mods
|
-- Global variable that can be used by other mods
|
||||||
-- Please see API.md for reference
|
-- Please see API.md for reference
|
||||||
noordstar_caves =
|
noordstar_caves = {}
|
||||||
{ a = nil
|
|
||||||
-- The `cave_vastness` is a variable that determines the size of caves.
|
|
||||||
-- The function takes in an x, y and z variable, and returns a number between
|
|
||||||
-- 0 and 1.
|
|
||||||
-- Low numbers mean very rare and tiny caves, while high numbers mean massive
|
|
||||||
-- caves, with massive open spaces.
|
|
||||||
-- If you wish to overwrite this function, it is good to keep in mind to:
|
|
||||||
-- - Make sure that the output changes VERY SLOWLY over time
|
|
||||||
-- - Keep the function performant as it will be executed many, many times
|
|
||||||
, cave_vastness = function(pos)
|
|
||||||
return pos.y / internal.world_depth
|
|
||||||
end
|
|
||||||
|
|
||||||
-- A public list of all registered biomes
|
-- Load features to influence cave shapes
|
||||||
, registered_biomes = {}
|
load("shape")
|
||||||
|
|
||||||
-- A public list of all registered shapes
|
-- Load features to influence cave biomes
|
||||||
, registered_shapes = {}
|
load("biome")
|
||||||
|
|
||||||
}
|
-- Start engine to generate caves
|
||||||
|
load("engine")
|
||||||
|
|
||||||
-- Remove all registered biomes and start with a clean slate
|
|
||||||
function noordstar_caves.clear_registered_biomes()
|
|
||||||
noordstar_caves.registered_biomes = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Remove all registered shapes and start with a clean slate
|
|
||||||
function noordstar_caves.clear_registered_shapes()
|
|
||||||
noordstar_caves.registered_shapes = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Register a new cave biome
|
|
||||||
function noordstar_caves.register_biome(def)
|
|
||||||
local d = internal.clean_biome_def(def)
|
|
||||||
|
|
||||||
if d then
|
|
||||||
noordstar_caves.registered_biomes[d.name] = d
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Register a new cave shape
|
|
||||||
function noordstar_caves.register_shape(def)
|
|
||||||
local d = internal.clean_shape_def(def)
|
|
||||||
|
|
||||||
if d then
|
|
||||||
noordstar_caves.registered_shapes[d.name] = d
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Override the world's depth
|
|
||||||
function noordstar_caves.set_world_depth(h)
|
|
||||||
internal.world_depth = h
|
|
||||||
end
|
|
||||||
noordstar_caves.set_world_depth(internal.world_depth)
|
|
||||||
|
|
||||||
-- Remove a specific registered cave biome
|
|
||||||
function noordstar_caves.unregister_biome(name)
|
|
||||||
noordstar_caves.registered_biomes[name] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Remove a specific registered cave shape
|
|
||||||
function noordstar_caves.unregister_shape(name)
|
|
||||||
noordstar_caves.registered_shapes[name] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calculate the distance of a given biome to a given point
|
|
||||||
function internal.biome_def_distance(def, heat, humidity)
|
|
||||||
local dx = math.abs(humidity - def.humidity_point)
|
|
||||||
local dy = math.abs(heat - def.heat_point)
|
|
||||||
|
|
||||||
return dx^2 + dy^2
|
|
||||||
end
|
|
||||||
|
|
||||||
-- While a public `noordstar_caves.cave_vastness` function is exposed,
|
|
||||||
-- this mod uses an improved internal function to keep the edges pleasant
|
|
||||||
-- and to avoid potential errors.
|
|
||||||
function internal.cave_vastness(pos)
|
|
||||||
-- Calculate value and put it in bounds
|
|
||||||
local v = noordstar_caves.cave_vastness(pos)
|
|
||||||
v = math.min(v, 1)
|
|
||||||
v = math.max(v, 0)
|
|
||||||
|
|
||||||
local d = internal.world_depth
|
|
||||||
|
|
||||||
if pos.y > d + 20 then
|
|
||||||
return v
|
|
||||||
elseif pos.y > d + 5 then
|
|
||||||
return v * (pos.y - (d + 5)) / 15
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Clean the user input on a biome definition before inserting it.
|
|
||||||
function internal.clean_biome_def(def)
|
|
||||||
if type(def.name) ~= "string" then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
if type(def.heat_point) ~= "number" then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
if type(def.humidity_point) ~= "number" then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local d = {
|
|
||||||
name = def.name,
|
|
||||||
heat_point = def.heat_point,
|
|
||||||
humidity_point = def.humidity_point,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Position
|
|
||||||
if type(def.min_pos) == "table" then
|
|
||||||
d.minp = {
|
|
||||||
x = def.min_pos.x or internal.world_minp.x,
|
|
||||||
y = def.min_pos.y or internal.world_minp.y,
|
|
||||||
z = def.min_pos.z or internal.world_minp.z,
|
|
||||||
}
|
|
||||||
elseif type(def.y_min) == "number" then
|
|
||||||
d.minp = {
|
|
||||||
x = internal.world_minp.x,
|
|
||||||
y = def.y_min,
|
|
||||||
z = internal.world_minp.z,
|
|
||||||
}
|
|
||||||
else
|
|
||||||
d.minp = {
|
|
||||||
x = internal.world_minp.x,
|
|
||||||
y = internal.world_minp.y,
|
|
||||||
z = internal.world_minp.z,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(def.max_pos) == "table" then
|
|
||||||
d.maxp = {
|
|
||||||
x = def.max_pos.x or internal.world_maxp.x,
|
|
||||||
y = def.max_pos.y or internal.world_maxp.y,
|
|
||||||
z = def.max_pos.z or internal.world_maxp.z,
|
|
||||||
}
|
|
||||||
elseif type(def.y_max) == "number" then
|
|
||||||
d.maxp = {
|
|
||||||
x = internal.world_maxp.x,
|
|
||||||
y = def.y_max,
|
|
||||||
z = internal.world_maxp.z,
|
|
||||||
}
|
|
||||||
else
|
|
||||||
d.maxp = {
|
|
||||||
x = internal.world_maxp.x,
|
|
||||||
y = internal.world_maxp.y,
|
|
||||||
z = internal.world_maxp.z,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Optional nodes
|
|
||||||
if type(def.node_dust) == "string" then
|
|
||||||
d.node_dust = def.node_dust
|
|
||||||
end
|
|
||||||
if type(def.node_floor) == "string" then
|
|
||||||
d.node_floor = def.node_floor
|
|
||||||
end
|
|
||||||
if type(def.node_wall) == "string" then
|
|
||||||
d.node_wall = def.node_wall
|
|
||||||
end
|
|
||||||
if type(def.node_roof) == "string" then
|
|
||||||
d.node_roof = def.node_roof
|
|
||||||
end
|
|
||||||
if type(def.node_air) == "string" then
|
|
||||||
d.node_air = def.node_air
|
|
||||||
end
|
|
||||||
if type(def.node_shell) == "string" then
|
|
||||||
d.node_shell = def.node_shell
|
|
||||||
end
|
|
||||||
if type(def.depth_shell) == "number" then
|
|
||||||
d.depth_shell = def.depth_shell
|
|
||||||
else
|
|
||||||
d.depth_shell = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
return d
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Clean the user input on a shape definition before inserting it.
|
|
||||||
function internal.clean_shape_def(def)
|
|
||||||
if type(def.name) ~= "string" then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
if type(def.connectivity_point) ~= "number" then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
if type(def.verticality_point) ~= "number" then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local d = {
|
|
||||||
name = def.name,
|
|
||||||
connectivity_point = def.connectivity_point,
|
|
||||||
verticality_point = def.verticality_point,
|
|
||||||
|
|
||||||
y_min = def.y_min or internal.world_minp.y,
|
|
||||||
y_max = def.y_max or internal.world_maxp.y,
|
|
||||||
}
|
|
||||||
|
|
||||||
if type(def.noise_params) == "table" then
|
|
||||||
d.noise_params = def.noise_params
|
|
||||||
end
|
|
||||||
|
|
||||||
local func = function(pos, v) return v end
|
|
||||||
if type(def.func) == "function" then
|
|
||||||
func = def.func
|
|
||||||
end
|
|
||||||
d.func = internal.enhanced_shape_func(def.name, func)
|
|
||||||
|
|
||||||
return d
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the most nearby cave shape
|
|
||||||
function internal.closest_cave_biome(heat, humidity)
|
|
||||||
local biome = internal.default_biome()
|
|
||||||
local d = internal.biome_def_distance(biome, heat, humidity)
|
|
||||||
|
|
||||||
for key, def in pairs(noordstar_caves.registered_biomes) do
|
|
||||||
local new_d = internal.biome_def_distance(def, heat, humidity)
|
|
||||||
|
|
||||||
if new_d <= d then
|
|
||||||
biome = def
|
|
||||||
d = new_d
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return shape
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the most nearby cave shape
|
|
||||||
function internal.closest_cave_shape(cnct, vrtcl)
|
|
||||||
local shape = internal.default_shape()
|
|
||||||
local d = internal.shape_def_distance(shape, cnct, vrtcl)
|
|
||||||
|
|
||||||
for key, def in pairs(noordstar_caves.registered_shapes) do
|
|
||||||
local new_d = internal.shape_def_distance(def, cnct, vrtcl)
|
|
||||||
|
|
||||||
if new_d <= d then
|
|
||||||
shape = def
|
|
||||||
d = new_d
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return shape
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get connectivity noise params
|
|
||||||
function internal.connectivity_noise_params()
|
|
||||||
local factor = math.max(
|
|
||||||
math.abs(#noordstar_caves.registered_shapes) ^ 0.5
|
|
||||||
, 1
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
offset = 50,
|
|
||||||
scale = 50,
|
|
||||||
spread =
|
|
||||||
internal.reduced_shape_pos(
|
|
||||||
{ x = factor * internal.cnct_blob.x
|
|
||||||
, y = factor * internal.cnct_blob.y
|
|
||||||
, z = factor * internal.cnct_blob.z
|
|
||||||
}
|
|
||||||
),
|
|
||||||
seed = internal.seeds.connectivity,
|
|
||||||
octaves = 2,
|
|
||||||
persistence = 0.2,
|
|
||||||
lacunarity = 2.0,
|
|
||||||
flags = "eased"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get a default cave biome in case no biomes are registered
|
|
||||||
function internal.default_biome()
|
|
||||||
return internal.clean_biome_def(
|
|
||||||
{ name = "noordstar_caves:default_biome"
|
|
||||||
, heat_point = internal.outlandish_point
|
|
||||||
, humidity_point = internal.outlandish_point
|
|
||||||
}
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get a default cave shape in case no shapes are registered
|
|
||||||
function internal.default_shape()
|
|
||||||
return internal.clean_shape_def(
|
|
||||||
{ name = "noordstar_caves:none"
|
|
||||||
, connectivity_point = internal.outlandish_point
|
|
||||||
, verticality_point = internal.outlandish_point
|
|
||||||
, func = function (pos, v) return 0 end
|
|
||||||
}
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- This library decides not to trust that the user is giving a proper input
|
|
||||||
-- on its function field. To help the developer insert a proper function, a few
|
|
||||||
-- error messages are inserted to give the developer more insight in how their
|
|
||||||
-- function behaves.
|
|
||||||
function internal.enhanced_shape_func(name, func)
|
|
||||||
if type(func) ~= "function" then
|
|
||||||
error("Expected function, found type `" .. type(func) .. "`")
|
|
||||||
end
|
|
||||||
|
|
||||||
return function (pos, n)
|
|
||||||
local out = func(pos, n)
|
|
||||||
|
|
||||||
if type(out) == "number" then
|
|
||||||
return out
|
|
||||||
elseif n == nil then
|
|
||||||
error(
|
|
||||||
table.concat(
|
|
||||||
{ "Shape"
|
|
||||||
, name
|
|
||||||
, "function must return a number."
|
|
||||||
, "Your function's input `n` was nil."
|
|
||||||
, "Perhaps your `noise_params` field is invalid?"
|
|
||||||
}
|
|
||||||
, " "
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else
|
|
||||||
error(
|
|
||||||
table.concat(
|
|
||||||
{ "Shape"
|
|
||||||
, name
|
|
||||||
, "function must return a number."
|
|
||||||
}
|
|
||||||
, " "
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get a Flat3dArray showing whether nodes are cave bools
|
|
||||||
function internal.flat_from_cave_bools(minp, maxp)
|
|
||||||
-- Calculate connectivity and verticality for each point
|
|
||||||
local connectivity = internal.flat_from_noise_params(
|
|
||||||
internal.connectivity_noise_params(),
|
|
||||||
internal.reduced_shape_pos(minp),
|
|
||||||
internal.reduced_shape_pos(maxp)
|
|
||||||
)
|
|
||||||
local verticality = internal.flat_from_noise_params(
|
|
||||||
internal.verticality_noise_params(),
|
|
||||||
internal.reduced_shape_pos(minp),
|
|
||||||
internal.reduced_shape_pos(maxp)
|
|
||||||
)
|
|
||||||
|
|
||||||
-- Using the calculated noise, determine the cave shape per shape chunk
|
|
||||||
local reduced = Flat3dArray:from_func(
|
|
||||||
internal.reduced_shape_pos(minp),
|
|
||||||
internal.reduced_shape_pos(maxp),
|
|
||||||
function (i, pos)
|
|
||||||
-- DEVELOPMENT ONLY: Unittest
|
|
||||||
if connectivity:pos_to_index(pos) ~= i then
|
|
||||||
error("Connectivity index doesn't match local index")
|
|
||||||
end
|
|
||||||
if verticality:pos_to_index(pos) ~= i then
|
|
||||||
error("Verticality index doesn't match local index")
|
|
||||||
end
|
|
||||||
-- DEVELOPMENT ONLY: End unittest
|
|
||||||
|
|
||||||
local cnct = connectivity:get_index(i)
|
|
||||||
local vrtcl = verticality:get_index(i)
|
|
||||||
|
|
||||||
local def = internal.closest_cave_shape(cnct, vrtcl)
|
|
||||||
|
|
||||||
return def.name
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Calculate noise for each shape
|
|
||||||
local noise = {}
|
|
||||||
for key, shape in pairs(noordstar_caves.registered_shapes) do
|
|
||||||
noise[key] = internal.flat_from_shape_def(shape, minp, maxp)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create a flat array of bools
|
|
||||||
local bools = Flat3dArray:from_func(minp, maxp, function (i, pos)
|
|
||||||
local key = reduced:get_pos(internal.reduced_shape_pos(pos))
|
|
||||||
|
|
||||||
local n = noise[key]:get_index(i)
|
|
||||||
local v = internal.cave_vastness(pos)
|
|
||||||
|
|
||||||
return internal.is_cave_node(n, v)
|
|
||||||
end)
|
|
||||||
|
|
||||||
return bools
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get a Flat3dArray using noise_params
|
|
||||||
function internal.flat_from_noise_params(noise_params, minp, maxp)
|
|
||||||
local nx = maxp.x - minp.x + 1
|
|
||||||
local ny = maxp.y - minp.y + 1
|
|
||||||
local nz = maxp.z - minp.z + 1
|
|
||||||
|
|
||||||
local buffer = {}
|
|
||||||
|
|
||||||
local p = PerlinNoiseMap(noise_params, { x = nx, y = ny, z = nz })
|
|
||||||
|
|
||||||
if nz == 1 then
|
|
||||||
p:get_2d_map_flat(minp, buffer)
|
|
||||||
else
|
|
||||||
p:get_3d_map_flat(minp, buffer)
|
|
||||||
end
|
|
||||||
|
|
||||||
return Flat3dArray:new(minp, maxp, buffer)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get a Flat3dArray about shape noise
|
|
||||||
-- This function is defined differently from other definitions because the
|
|
||||||
-- noise_params are optional in the shape definition
|
|
||||||
function internal.flat_from_shape_def(def, minp, maxp)
|
|
||||||
local noise_flat_map = {}
|
|
||||||
|
|
||||||
local nx = maxp.x - minp.x + 1
|
|
||||||
local ny = maxp.y - minp.y + 1
|
|
||||||
local nz = maxp.z - minp.z + 1
|
|
||||||
|
|
||||||
if def.noise_params then
|
|
||||||
local p = PerlinNoiseMap(def.noise_params, { x = nx, y = ny, z = nz })
|
|
||||||
|
|
||||||
if nz == 1 then
|
|
||||||
p:get_2d_map_flat(minp, noise_flat_map)
|
|
||||||
else
|
|
||||||
p:get_3d_map_flat(minp, noise_flat_map)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
internal.iter_3d_area(minp, maxp, function (i, pos)
|
|
||||||
noise_flat_map[i] = def.func(pos, noise_flat_map[i])
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Convert 3d relative coordinates to an index on a flat array
|
|
||||||
function internal.from_3d_to_flat(dx, dy, dz, nx, ny)
|
|
||||||
return (nx * ny * dz) + (nx * dy) + dx + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the noise params for the cave biome temperature.
|
|
||||||
function internal.heat_noise_params()
|
|
||||||
return {
|
|
||||||
offset = 50,
|
|
||||||
scale = 50,
|
|
||||||
spread = internal.biome_size,
|
|
||||||
seed = internal.seeds.heat,
|
|
||||||
octaves = 2,
|
|
||||||
persistence = 0.1,
|
|
||||||
lacunarity = 2.0,
|
|
||||||
flags = ""
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the noise params for the cave biome humidity.
|
|
||||||
function internal.humidity_noise_params()
|
|
||||||
return {
|
|
||||||
offset = 50,
|
|
||||||
scale = 50,
|
|
||||||
spread = internal.biome_size,
|
|
||||||
seed = internal.seeds.humidity,
|
|
||||||
octaves = 2,
|
|
||||||
persistence = 0.1,
|
|
||||||
lacunarity = 2.0,
|
|
||||||
flags = ""
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Return whether a node is part of a cave
|
|
||||||
function internal.is_cave_node(noise, vastness)
|
|
||||||
return noise > 1 - vastness
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Return a bool whether a position is within a given volume
|
|
||||||
function internal.is_valid_pos(pos, minp, maxp)
|
|
||||||
if minp.x <= pos.x and pos.x <= maxp.x then
|
|
||||||
if minp.y <= pos.y and pos.y <= maxp.y then
|
|
||||||
if minp.z <= pos.z and pos.z <= maxp.z then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Iterate over a 3d area, both indexing by the index and the ansolute position
|
|
||||||
function internal.iter_3d_area(minp, maxp, callback)
|
|
||||||
local nx = maxp.x - minp.x + 1
|
|
||||||
local ny = maxp.y - minp.y + 1
|
|
||||||
-- local nz = maxp.z - minp.z + 1
|
|
||||||
|
|
||||||
local i = 0
|
|
||||||
|
|
||||||
for z = minp.z, maxp.z, 1 do
|
|
||||||
for y = minp.y, maxp.y, 1 do
|
|
||||||
for x = minp.x, maxp.x, 1 do
|
|
||||||
local pos = { x = x, y = y, z = z }
|
|
||||||
i = i + 1
|
|
||||||
|
|
||||||
-- DEVELOPMENT ONLY: This function only serves as a unittest
|
|
||||||
-- DEVELOPMENT ONLY: to verify that the iteration across a 3D
|
|
||||||
-- DEVELOPMENT ONLY: table works appropriately.
|
|
||||||
-- DEVELOPMENT ONLY: This calculation is highly inefficient and
|
|
||||||
-- DEVELOPMENT ONLY: unnecessary so it is highly recommended
|
|
||||||
-- DEVELOPMENT ONLY: to remove this code in production.
|
|
||||||
local dx = x - minp.x
|
|
||||||
local dy = y - minp.y
|
|
||||||
local dz = z - minp.z
|
|
||||||
local si = internal.from_3d_to_flat(dx, dy, dz, nx, ny)
|
|
||||||
|
|
||||||
if i ~= si then
|
|
||||||
error(
|
|
||||||
table.concat(
|
|
||||||
{ "Expected position"
|
|
||||||
, internal.pos_to_str(pos)
|
|
||||||
, "to correspond to index"
|
|
||||||
, si
|
|
||||||
, "but our iteration arrived at index"
|
|
||||||
, i
|
|
||||||
}
|
|
||||||
, " "
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
-- DEVELOPMENT ONLY:
|
|
||||||
-- DEVELOPMENT ONLY: This is the bottom of the unittest.
|
|
||||||
-- DEVELOPMENT ONLY: Please remove this code in a future commit
|
|
||||||
-- DEVELOPMENT ONLY: before the mod is assumed to be
|
|
||||||
-- DEVELOPMENT ONLY: production-ready.
|
|
||||||
-- DEVELOPMENT ONLY:
|
|
||||||
|
|
||||||
callback(i, pos)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Helper function to convert a set of coordinates to a readable string
|
|
||||||
function internal.pos_to_str(pos)
|
|
||||||
return "(" .. pos.x .. ", " .. pos.y .. ", " .. pos.z .. " )"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get a reduced position based on the chunks
|
|
||||||
function internal.reduced_shape_pos(pos)
|
|
||||||
return
|
|
||||||
{ x = math.floor(pos.x / internal.shape_cluster.x)
|
|
||||||
, y = math.floor(pos.y / internal.shape_cluster.y)
|
|
||||||
, z = math.floor(pos.z / internal.shape_cluster.z)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calculate the relative index change to move downwards one position
|
|
||||||
function internal.relative_get_down(nx, ny)
|
|
||||||
return internal.relative_index_diff(0, -1, 0, nx, ny)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calculate the relative index change to move eastwards one position
|
|
||||||
function internal.relative_get_east(nx, ny)
|
|
||||||
return internal.relative_index_diff(1, 0, 0, nx, ny)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calculate the relative index change to move northwards one position
|
|
||||||
function internal.relative_get_north(nx, ny)
|
|
||||||
return internal.relative_index_diff(0, 0, 1, nx, ny)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calculate the relative index change to move southwards one position
|
|
||||||
function internal.relative_get_south(nx, ny)
|
|
||||||
return internal.relative_index_diff(0, 0, -1, nx, ny)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calculate the relative index change to move upwards one position
|
|
||||||
function internal.relative_get_up(nx, ny)
|
|
||||||
return internal.relative_index_diff(0, 1, 0, nx, ny)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calculate the relative index change to move westwards one position
|
|
||||||
function internal.relative_get_west(nx, ny)
|
|
||||||
return internal.relative_index_diff(-1, 0, 0, nx, ny)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calculate the difference in index to relative coordinates.
|
|
||||||
-- Used to speed up index lookup on flat 3d arrays.
|
|
||||||
function internal.relative_index_diff(dx, dy, dz, nx, ny)
|
|
||||||
return
|
|
||||||
( internal.from_3d_to_flat(dx, dy, dz, nx, ny)
|
|
||||||
- internal.from_3d_to_flat( 0, 0, 0, nx, ny)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calculate the distance of a given shape to a given point
|
|
||||||
function internal.shape_def_distance(def, cnct, vrtcl)
|
|
||||||
local dx = math.abs(cnct - def.connectivity_point)
|
|
||||||
local dy = math.abs(vrtcl - def.verticality_point)
|
|
||||||
|
|
||||||
return dx^2 + dy^2
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get verticality noise params
|
|
||||||
function internal.verticality_noise_params()
|
|
||||||
local factor = math.max(
|
|
||||||
math.abs(#noordstar_caves.registered_shapes) ^ 0.5
|
|
||||||
, 1
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
offset = 50,
|
|
||||||
scale = 50,
|
|
||||||
spread =
|
|
||||||
internal.reduced_shape_pos(
|
|
||||||
{ x = factor * internal.vrtcl_blob.x
|
|
||||||
, y = factor * internal.vrtcl_blob.y
|
|
||||||
, z = factor * internal.vrtcl_blob.z
|
|
||||||
}
|
|
||||||
),
|
|
||||||
seed = internal.seeds.verticality,
|
|
||||||
octaves = 2,
|
|
||||||
persistence = 0.2,
|
|
||||||
lacunarity = 2.0,
|
|
||||||
flags = "eased"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Determine whether a position is within given bounds
|
|
||||||
function internal.within_bounds(pos, minp, maxp)
|
|
||||||
if minp.x <= pos.x and pos.x <= maxp.x then
|
|
||||||
if minp.y <= pos.y and pos.y <= maxp.y then
|
|
||||||
if minp.z <= pos.z and pos.z <= maxp.z then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create a new flat 3d array based on a function callback that gets the index
|
|
||||||
-- and position as inputs
|
|
||||||
function Flat3dArray:from_func(minp, maxp, callback)
|
|
||||||
local arr = {}
|
|
||||||
|
|
||||||
internal.iter_3d_area(minp, maxp, function (i, pos)
|
|
||||||
arr[i] = callback(i, pos)
|
|
||||||
end)
|
|
||||||
|
|
||||||
return self:new(minp, maxp, arr)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the Flat3dArray's value on the i'th index
|
|
||||||
function Flat3dArray:get_index(i)
|
|
||||||
local out = self.arr[i]
|
|
||||||
if out == nil then
|
|
||||||
error(
|
|
||||||
"Index " .. i .. " not found in array of length " .. #self.arr
|
|
||||||
)
|
|
||||||
end
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get a value at a given position
|
|
||||||
function Flat3dArray:get_pos(pos)
|
|
||||||
-- pos_to_index already validates so there's no need to validate here
|
|
||||||
return self:get_index(self:pos_to_index(pos))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create a new Flat3dArray instance
|
|
||||||
function Flat3dArray:new(minp, maxp, arr)
|
|
||||||
local instance = {}
|
|
||||||
setmetatable(instance, Flat3dArray)
|
|
||||||
|
|
||||||
local nx = maxp.x - minp.x + 1
|
|
||||||
local ny = maxp.y - minp.y + 1
|
|
||||||
local nz = maxp.z - minp.z + 1
|
|
||||||
|
|
||||||
if #arr ~= nx * ny * nz then
|
|
||||||
error(
|
|
||||||
"Input array doesn't match dimension lengths: " .. nx .. " x " ..
|
|
||||||
ny .. " x " .. nz .. " = " .. (nx*ny*nz) .. ", but found " .. #arr
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Variables
|
|
||||||
instance.nx = nx
|
|
||||||
instance.ny = ny
|
|
||||||
instance.nz = nz
|
|
||||||
instance.minp = minp
|
|
||||||
instance.maxp = maxp
|
|
||||||
instance.arr = arr
|
|
||||||
|
|
||||||
-- Constants
|
|
||||||
instance.up = internal.relative_get_up(nx, ny)
|
|
||||||
instance.down = internal.relative_get_down(nx, ny)
|
|
||||||
instance.north = internal.relative_get_north(nx, ny)
|
|
||||||
instance.east = internal.relative_get_east(nx, ny)
|
|
||||||
instance.south = internal.relative_get_south(nx, ny)
|
|
||||||
instance.west = internal.relative_get_west(nx, ny)
|
|
||||||
|
|
||||||
return instance
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Convert a position to an index
|
|
||||||
function Flat3dArray:pos_to_index(pos)
|
|
||||||
self:validate_pos(pos)
|
|
||||||
|
|
||||||
local dx = pos.x - self.minp.x
|
|
||||||
local dy = pos.y - self.minp.y
|
|
||||||
local dz = pos.z - self.minp.z
|
|
||||||
|
|
||||||
return internal.from_3d_to_flat(dx, dy, dz, self.nx, self.ny)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Override a value at a given index
|
|
||||||
function Flat3dArray:set_index(i, val)
|
|
||||||
self:validate_index(i)
|
|
||||||
self.arr[i] = val
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Validate an index to be in the Flat3dArray
|
|
||||||
function Flat3dArray:validate_index(i)
|
|
||||||
if i < 1 or i > #self.arr then
|
|
||||||
error(
|
|
||||||
table.concat(
|
|
||||||
{ "Index"
|
|
||||||
, i
|
|
||||||
, "is not in range 1 -"
|
|
||||||
, #self.arr
|
|
||||||
}
|
|
||||||
, " "
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Validate a position to be in the Flat3dArray
|
|
||||||
function Flat3dArray:validate_pos(pos)
|
|
||||||
if not internal.is_valid_pos(pos, self.minp, self.maxp) then
|
|
||||||
error(
|
|
||||||
table.concat(
|
|
||||||
{ "Position"
|
|
||||||
, internal.pos_to_str(pos)
|
|
||||||
, "is not within minp ="
|
|
||||||
, internal.pos_to_str(self.minp)
|
|
||||||
, "and maxp ="
|
|
||||||
, internal.pos_to_str(self.maxp)
|
|
||||||
}
|
|
||||||
, " "
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- For show, the following code COULD in theory be run elsewhere
|
-- For show, the following code COULD in theory be run elsewhere
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
noordstar_caves.register_shape({
|
|
||||||
name = "noordstar_caves:bubbles",
|
|
||||||
|
|
||||||
noise_params = {
|
|
||||||
offset = 0.5,
|
|
||||||
scale = 0.5,
|
|
||||||
spread = { x = 20, y = 20, z = 20 },
|
|
||||||
seed = 248039,
|
|
||||||
octaves = 2,
|
|
||||||
persistence = 0.6,
|
|
||||||
lacunarity = 2.0,
|
|
||||||
flags = "eased"
|
|
||||||
},
|
|
||||||
|
|
||||||
func = function(pos, n)
|
|
||||||
return n
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- TODO: Implement y limits
|
|
||||||
-- y_min = -31000,
|
|
||||||
-- y_max = 31000,
|
|
||||||
|
|
||||||
connectivity_point = 10,
|
|
||||||
verticality_point = 40,
|
|
||||||
})
|
|
||||||
-- noordstar_caves.register_shape({
|
|
||||||
-- name = "noordstar_caves:cliffs",
|
|
||||||
|
|
||||||
-- noise_params = {
|
|
||||||
-- offset = 0.4,
|
|
||||||
-- scale = 0.5,
|
|
||||||
-- spread = { x = 20, y = 100, z = 20 },
|
|
||||||
-- seed = 97354,
|
|
||||||
-- octaves = 4,
|
|
||||||
-- persistence = 0.6,
|
|
||||||
-- lacunarity = 2.0,
|
|
||||||
-- flags = ""
|
|
||||||
-- },
|
|
||||||
|
|
||||||
-- func = function(pos, n)
|
|
||||||
-- return n
|
|
||||||
-- end,
|
|
||||||
|
|
||||||
-- connectivity_point = 30,
|
|
||||||
-- verticality_point = 80,
|
|
||||||
-- })
|
|
||||||
-- noordstar_caves.register_shape({
|
|
||||||
-- name = "noordstar_caves:donuts",
|
|
||||||
|
|
||||||
-- noise_params = {
|
|
||||||
-- offset = 0.0,
|
|
||||||
-- scale = 1.0,
|
|
||||||
-- spread = { x = 30, y = 30, z = 30 },
|
|
||||||
-- seed = 3934,
|
|
||||||
-- octaves = 1,
|
|
||||||
-- persistence = 0.6,
|
|
||||||
-- lacunarity = 2.0,
|
|
||||||
-- flags = "eased"
|
|
||||||
-- },
|
|
||||||
|
|
||||||
-- func = function(pos, n)
|
|
||||||
-- return 1 - 2 * math.abs(n)^0.5
|
|
||||||
-- end,
|
|
||||||
|
|
||||||
-- connectivity_point = 50,
|
|
||||||
-- verticality_point = 40,
|
|
||||||
-- })
|
|
||||||
-- noordstar_caves.register_shape({
|
|
||||||
-- name = "noordstar_caves:wall",
|
|
||||||
|
|
||||||
-- func = function(pos, n)
|
|
||||||
-- return -0.5
|
|
||||||
-- end,
|
|
||||||
|
|
||||||
-- connectivity_point = 0,
|
|
||||||
-- verticality_point = 0,
|
|
||||||
-- })
|
|
||||||
|
|
||||||
-- noordstar_caves.set_world_depth(-60)
|
|
||||||
-- noordstar_caves.cave_vastness = function(pos) return math.abs(pos.y - 60) / 120 end
|
|
||||||
|
|
||||||
noordstar_caves.register_biome({
|
|
||||||
name = "test",
|
|
||||||
node_floor = "mcl_core:crying_obsidian",
|
|
||||||
node_wall = "mcl_core:sand",
|
|
||||||
node_roof = "mcl_ocean:sea_lantern",
|
|
||||||
heat_point = 0,
|
|
||||||
humidity_point = 0,
|
|
||||||
})
|
|
||||||
noordstar_caves.register_biome({
|
|
||||||
name = "test2",
|
|
||||||
node_floor = "mcl_amethyst:amethyst_block",
|
|
||||||
node_wall = "mcl_crimson:shroomlight",
|
|
||||||
node_roof = "mcl_colorblocks:glazed_terracotta_silver",
|
|
||||||
heat_point = 100,
|
|
||||||
humidity_point = 100,
|
|
||||||
})
|
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
-- This file takes care of cave biomes
|
||||||
|
|
||||||
|
noordstar_caves.registered_biomes = {}
|
||||||
|
|
||||||
|
-- Clean the input and return a valid shape def
|
||||||
|
-- If the input is invalid, return nil
|
||||||
|
local function clean_def(def)
|
||||||
|
if type(def.name) ~= "string" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if type(def.heat_point) ~= "number" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if type(def.humidity_point) ~= "number" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local d = {
|
||||||
|
name = def.name,
|
||||||
|
heat_point = def.heat_point,
|
||||||
|
humidity_point = def.humidity_point,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Position
|
||||||
|
if type(def.min_pos) == "table" then
|
||||||
|
d.minp = {
|
||||||
|
x = def.min_pos.x or -1e5,
|
||||||
|
y = def.min_pos.y or -1e5,
|
||||||
|
z = def.min_pos.z or -1e5,
|
||||||
|
}
|
||||||
|
elseif type(def.y_min) == "number" then
|
||||||
|
d.minp = { x = -1e5, y = def.y_min, z = -1e5 }
|
||||||
|
else
|
||||||
|
d.minp = { x = -1e5, y = -1e5, z = -1e5 }
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(def.max_pos) == "table" then
|
||||||
|
d.maxp = {
|
||||||
|
x = def.max_pos.x or 1e5,
|
||||||
|
y = def.max_pos.y or 1e5,
|
||||||
|
z = def.max_pos.z or 1e5,
|
||||||
|
}
|
||||||
|
elseif type(def.y_max) == "number" then
|
||||||
|
d.maxp = { x = 1e5, y = def.y_max, z = 1e5 }
|
||||||
|
else
|
||||||
|
d.maxp = { x = 1e5, y = 1e5, z = 1e5 }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Optional nodes
|
||||||
|
if type(def.node_dust) == "string" then
|
||||||
|
d.node_dust = def.node_dust
|
||||||
|
end
|
||||||
|
if type(def.node_floor) == "string" then
|
||||||
|
d.node_floor = def.node_floor
|
||||||
|
end
|
||||||
|
if type(def.node_wall) == "string" then
|
||||||
|
d.node_wall = def.node_wall
|
||||||
|
end
|
||||||
|
if type(def.node_roof) == "string" then
|
||||||
|
d.node_roof = def.node_roof
|
||||||
|
end
|
||||||
|
if type(def.node_air) == "string" then
|
||||||
|
d.node_air = def.node_air
|
||||||
|
end
|
||||||
|
if type(def.node_shell) == "string" then
|
||||||
|
d.node_shell = def.node_shell
|
||||||
|
end
|
||||||
|
if type(def.depth_shell) == "number" then
|
||||||
|
d.depth_shell = def.depth_shell
|
||||||
|
else
|
||||||
|
d.depth_shell = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return d
|
||||||
|
end
|
||||||
|
|
||||||
|
function noordstar_caves.register_biome(def)
|
||||||
|
local d = clean_def(def)
|
||||||
|
|
||||||
|
if d ~= nil then
|
||||||
|
noordstar_caves.registered_biomes[d.name] = d
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function noordstar_caves.unregister_biome(name)
|
||||||
|
noordstar_caves.registered_biomes[name] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function noordstar_caves.clear_registered_biomes()
|
||||||
|
noordstar_caves.registered_biomes = {}
|
||||||
|
end
|
119
lua/engine.lua
119
lua/engine.lua
|
@ -37,19 +37,6 @@ local default_biome =
|
||||||
, maxp = { x = 1e5, y = 1e5, z = 1e5 }
|
, maxp = { x = 1e5, y = 1e5, z = 1e5 }
|
||||||
}
|
}
|
||||||
|
|
||||||
local cave_shape_chunk_size = 8
|
|
||||||
|
|
||||||
local cluster_shape_chunks_x = 4
|
|
||||||
local cluster_shape_chunks_y = 6
|
|
||||||
local cluster_shape_chunks_z = 4
|
|
||||||
|
|
||||||
local function reduced_pos(pos)
|
|
||||||
return
|
|
||||||
{ x = math.floor(pos.x / (cave_shape_chunk_size * cluster_shape_chunks_x))
|
|
||||||
, y = math.floor(pos.y / (cave_shape_chunk_size * cluster_shape_chunks_y))
|
|
||||||
, z = math.floor(pos.z / (cave_shape_chunk_size * cluster_shape_chunks_z))
|
|
||||||
}
|
|
||||||
end
|
|
||||||
-- Convert 3d relative coordinates to an index on a flat array
|
-- Convert 3d relative coordinates to an index on a flat array
|
||||||
local function from_3d_to_flat(dx, dy, dz, nx, ny)
|
local function from_3d_to_flat(dx, dy, dz, nx, ny)
|
||||||
return (nx * ny * dz) + (nx * dy) + dx + 1
|
return (nx * ny * dz) + (nx * dy) + dx + 1
|
||||||
|
@ -265,13 +252,7 @@ local function get_connectivity_noise_params(shape_size)
|
||||||
return {
|
return {
|
||||||
offset = 50,
|
offset = 50,
|
||||||
scale = 50,
|
scale = 50,
|
||||||
spread =
|
spread = { x = factor * 250, y = factor * 100, z = factor * 250 },
|
||||||
reduced_pos(
|
|
||||||
{ x = factor * 250
|
|
||||||
, y = factor * 100
|
|
||||||
, z = factor * 250
|
|
||||||
}
|
|
||||||
),
|
|
||||||
seed = 297948,
|
seed = 297948,
|
||||||
octaves = 2,
|
octaves = 2,
|
||||||
persistence = 0.2,
|
persistence = 0.2,
|
||||||
|
@ -288,13 +269,7 @@ local function get_verticality_noise_params(shape_size)
|
||||||
return {
|
return {
|
||||||
offset = 50,
|
offset = 50,
|
||||||
scale = 50,
|
scale = 50,
|
||||||
spread =
|
spread = { x = factor * 100, y = factor * 250, z = factor * 100 },
|
||||||
reduced_pos(
|
|
||||||
{ x = factor * 100
|
|
||||||
, y = factor * 250
|
|
||||||
, z = factor * 100
|
|
||||||
}
|
|
||||||
),
|
|
||||||
seed = 35644,
|
seed = 35644,
|
||||||
octaves = 2,
|
octaves = 2,
|
||||||
persistence = 0.2,
|
persistence = 0.2,
|
||||||
|
@ -303,30 +278,26 @@ local function get_verticality_noise_params(shape_size)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get whether a cave shape is within distance.
|
-- Get the distance of each cave shape to calculate their weight.
|
||||||
local function shape_within_distance(shape_size, dx, dy)
|
local function shape_distance_weight(shape_size, dx, dy)
|
||||||
local factor = math.max(math.abs(shape_size) ^ 0.5, 1)
|
local factor = math.max(math.abs(shape_size) ^ 0.5, 1)
|
||||||
local max_distance = 30 / factor
|
local max_distance = 100 / factor
|
||||||
|
|
||||||
return dx^2 + dy^2 <= max_distance^2
|
if dx^2 + dy^2 > max_distance^2 then
|
||||||
end
|
return 0
|
||||||
|
else
|
||||||
local function shape_def_distance(def, x, y)
|
return 1 / (dx^5 + dy^5)
|
||||||
local dx = math.abs(x - def.connectivity_point)
|
end
|
||||||
local dy = math.abs(y - def.verticality_point)
|
|
||||||
|
|
||||||
return dx^2 + dy^2
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get a flat map containing all flat threshold values
|
-- Get a flat map containing all flat threshold values
|
||||||
local function get_threshold_flat(minp, maxp)
|
local function get_threshold_flat(minp, maxp)
|
||||||
local connectivity = get_flat_from_noise_params(
|
local connectivity = get_flat_from_noise_params(
|
||||||
reduced_pos(minp), reduced_pos(maxp),
|
minp, maxp,
|
||||||
get_connectivity_noise_params(#noordstar_caves.registered_shapes)
|
get_connectivity_noise_params(#noordstar_caves.registered_shapes)
|
||||||
)
|
)
|
||||||
|
|
||||||
local verticality = get_flat_from_noise_params(
|
local verticality = get_flat_from_noise_params(
|
||||||
reduced_pos(minp), reduced_pos(maxp),
|
minp, maxp,
|
||||||
get_verticality_noise_params(#noordstar_caves.registered_shapes)
|
get_verticality_noise_params(#noordstar_caves.registered_shapes)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -334,37 +305,41 @@ local function get_threshold_flat(minp, maxp)
|
||||||
|
|
||||||
-- Get noise for all cave shapes
|
-- Get noise for all cave shapes
|
||||||
for key, def in pairs(noordstar_caves.registered_shapes) do
|
for key, def in pairs(noordstar_caves.registered_shapes) do
|
||||||
noise[key] = get_flat_from_shape_def(def, reduced_pos(minp), reduced_pos(maxp))
|
noise[key] = {
|
||||||
|
key = key,
|
||||||
|
noise = get_flat_from_shape_def(def, minp, maxp),
|
||||||
|
def = def
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create a (reduced) flat array
|
|
||||||
local reduced = Flat3dArray:from_func(
|
|
||||||
reduced_pos(minp), reduced_pos(maxp),
|
|
||||||
function(i, pos)
|
|
||||||
local x = connectivity:get_pos(pos)
|
|
||||||
local y = verticality:get_pos(pos)
|
|
||||||
|
|
||||||
local value = 0
|
|
||||||
|
|
||||||
for key, def in pairs(noordstar_caves.registered_shapes) do
|
|
||||||
local dx = math.abs(x - def.connectivity_point)
|
|
||||||
local dy = math.abs(y - def.verticality_point)
|
|
||||||
|
|
||||||
if shape_within_distance(#noordstar_caves.registered_shapes, dx, dy) then
|
|
||||||
value = math.max(value, noise[key]:get_pos(pos))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return value
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
-- Create the flat array
|
-- Create the flat array
|
||||||
local x = Flat3dArray:from_func(minp, maxp, function(i, pos)
|
return Flat3dArray:from_func(minp, maxp, function(i, pos)
|
||||||
return reduced:get_pos(reduced_pos(pos))
|
local total = 0
|
||||||
end)
|
local count = 0
|
||||||
|
|
||||||
return x
|
local x = connectivity:get_pos(pos)
|
||||||
|
local y = verticality:get_pos(pos)
|
||||||
|
|
||||||
|
for _, n in pairs(noise) do
|
||||||
|
local v = n.noise:get_pos(pos)
|
||||||
|
|
||||||
|
local dx = math.abs(x - n.def.connectivity_point)
|
||||||
|
local dy = math.abs(y - n.def.verticality_point)
|
||||||
|
|
||||||
|
local w = math.abs(shape_distance_weight(
|
||||||
|
#noordstar_caves.registered_shapes, dx, dy
|
||||||
|
))
|
||||||
|
|
||||||
|
total = total + v * w
|
||||||
|
count = count + w
|
||||||
|
end
|
||||||
|
|
||||||
|
if count <= 0 then
|
||||||
|
return -1000
|
||||||
|
else
|
||||||
|
return total / count
|
||||||
|
end
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -495,13 +470,13 @@ local function biome_def_to_content_id(def, nt, original_block)
|
||||||
|
|
||||||
local node_air = get_node(def.node_air, minetest.get_content_id("air"))
|
local node_air = get_node(def.node_air, minetest.get_content_id("air"))
|
||||||
local node_floor = get_node(def.node_floor, original_block)
|
local node_floor = get_node(def.node_floor, original_block)
|
||||||
local node_stone = get_node(nil, original_block)
|
local node_stone = original_block
|
||||||
local node_roof = get_node(def.node_roof, original_block)
|
local node_roof = get_node(def.node_roof, original_block)
|
||||||
local node_unknown = get_node(nil, original_block)
|
local node_unknown = original_block
|
||||||
local node_wall = get_node(def.node_wall, original_block)
|
local node_wall = get_node(def.node_wall, original_block)
|
||||||
|
|
||||||
if nt == node_type.unknown then
|
if nt == node_type.unknown then
|
||||||
return node_unknown()
|
return node_unknown
|
||||||
elseif nt == node_type.floor then
|
elseif nt == node_type.floor then
|
||||||
return node_floor()
|
return node_floor()
|
||||||
elseif nt == node_type.wall then
|
elseif nt == node_type.wall then
|
||||||
|
@ -511,7 +486,7 @@ local function biome_def_to_content_id(def, nt, original_block)
|
||||||
elseif nt == node_type.content then
|
elseif nt == node_type.content then
|
||||||
return node_air()
|
return node_air()
|
||||||
elseif nt == node_type.stone then
|
elseif nt == node_type.stone then
|
||||||
return node_stone()
|
return node_stone
|
||||||
else
|
else
|
||||||
return original_block
|
return original_block
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
-- This file takes care of the cave shapes.
|
||||||
|
|
||||||
|
noordstar_caves.registered_shapes = {}
|
||||||
|
|
||||||
|
-- Clean the input and return a valid shape def
|
||||||
|
-- If the input is invalid, return nil
|
||||||
|
local function clean_def(def)
|
||||||
|
if type(def.name) ~= "string" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if type(def.connectivity_point) ~= "number" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if type(def.verticality_point) ~= "number" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local d = {
|
||||||
|
name = def.name,
|
||||||
|
connectivity_point = def.connectivity_point,
|
||||||
|
verticality_point = def.verticality_point
|
||||||
|
}
|
||||||
|
|
||||||
|
if type(def.noise_params) == "table" then
|
||||||
|
d.noise_params = def.noise_params
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(def.func) == "function" then
|
||||||
|
d.func = def.func
|
||||||
|
else
|
||||||
|
d.func = function(pos, v) return v end
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(def.y_min) == "number" and type(def.y_max) == "number" then
|
||||||
|
d.y_min = def.y_min
|
||||||
|
d.y_max = def.y_max
|
||||||
|
end
|
||||||
|
|
||||||
|
return d
|
||||||
|
end
|
||||||
|
|
||||||
|
function noordstar_caves.register_shape(def)
|
||||||
|
local d = clean_def(def)
|
||||||
|
|
||||||
|
if d ~= nil then
|
||||||
|
noordstar_caves.registered_shapes[d.name] = d
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function noordstar_caves.unregister_shape(name)
|
||||||
|
noordstar_caves.registered_shapes[name] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function noordstar_caves.clear_registered_shapes()
|
||||||
|
noordstar_caves.registered_shapes = {}
|
||||||
|
end
|
Loading…
Reference in New Issue