diff --git a/res/generators/default.lua b/res/generators/default.lua index 21fae723..393de9f5 100644 --- a/res/generators/default.lua +++ b/res/generators/default.lua @@ -19,6 +19,20 @@ biomes = { {block="base:stone", height=-1}, {block="base:bazalt", height=1}, } + }, + desert = { + parameters = { + {value=0.0, weight=1.0}, + {value=0.5, weight=1.0}, + }, + sea_layers = { + {block="base:water", height=-1}, + }, + layers = { + {block="base:sand", height=6}, + {block="base:stone", height=-1}, + {block="base:bazalt", height=1}, + } } } @@ -65,3 +79,10 @@ function generate_heightmap(x, y, w, h, seed) map:crop(0, 0, w, h) return map end + +function generate_biome_parameters(x, y, w, h, seed) + local tempmap = Heightmap(w, h) + tempmap:noise({x, y}, 0.4, 3) + local hummap = Heightmap(w, h) + return tempmap, hummap +end diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index d14a7fc8..8efdf0bb 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -9,6 +9,8 @@ #include "voxels/Chunk.hpp" #include "world/generator/GeneratorDef.hpp" +// TODO: use dynamic::* for parsing + class LuaGeneratorScript : public GeneratorScript { scriptenv env; std::vector biomes; @@ -45,6 +47,34 @@ public: return std::make_shared(size.x, size.y); } + std::vector> generateParameterMaps( + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed + ) override { + std::vector> maps; + + auto L = lua::get_main_thread(); + lua::pushenv(L, *env); + if (lua::getfield(L, "generate_biome_parameters")) { + lua::pushivec_stack(L, offset); + lua::pushivec_stack(L, size); + lua::pushinteger(L, seed); + if (lua::call_nothrow(L, 5, biomeParameters)) { + for (int i = biomeParameters-1; i >= 0; i--) { + maps.push_back( + lua::touserdata(L, -1-i)->getHeightmap()); + + } + lua::pop(L, 1+biomeParameters); + return maps; + } + } + lua::pop(L); + for (uint i = 0; i < biomeParameters; i++) { + maps.push_back(std::make_shared(size.x, size.y)); + } + return maps; + } + void prepare(const Content* content) override { for (auto& biome : biomes) { for (auto& layer : biome.groundLayers.layers) { @@ -118,7 +148,7 @@ static inline Biome load_biome( ) { lua::pushvalue(L, idx); - std::vector parameters(parametersCount); + std::vector parameters; lua::requirefield(L, "parameters"); if (lua::objlen(L, -1) < parametersCount) { throw std::runtime_error( diff --git a/src/maths/Heightmap.hpp b/src/maths/Heightmap.hpp index 5b9f4cfa..52a3d394 100644 --- a/src/maths/Heightmap.hpp +++ b/src/maths/Heightmap.hpp @@ -37,6 +37,14 @@ public: return height; } + float get(uint x, uint y) { + return buffer.at(y * width + x); + } + + float getUnchecked(uint x, uint y) { + return buffer[y * width + x]; + } + float* getValues() { return buffer.data(); } diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index dcc8220b..7b469bd3 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -61,6 +61,9 @@ public: virtual std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; + virtual std::vector> generateParameterMaps( + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; + virtual const std::vector& getBiomes() const = 0; /// @return Number of biome parameters, that biome choosing depending on diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index c18747eb..eeea5f64 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -9,6 +9,8 @@ #include "voxels/voxel.hpp" #include "world/generator/GeneratorDef.hpp" +static inline constexpr uint MAX_PARAMETERS = 16; + WorldGenerator::WorldGenerator(const GeneratorDef& def, const Content* content) : def(def), content(content) { } @@ -30,7 +32,6 @@ static inline void generate_pole( layerExtension = std::max(0, layer.height); continue; } - int layerHeight = layer.height; if (layerHeight == -1) { // resizeable layer @@ -47,6 +48,32 @@ static inline void generate_pole( } } +static inline const Biome* choose_biome( + const std::vector& biomes, + const std::vector>& maps, + uint x, uint z +) { + uint paramsCount = maps.size(); + float params[MAX_PARAMETERS]; + for (uint i = 0; i < paramsCount; i++) { + params[i] = maps[i]->getUnchecked(x, z); + } + const Biome* chosenBiome = nullptr; + float chosenScore = std::numeric_limits::infinity(); + for (const auto& biome : biomes) { + float score = 0.0f; + for (uint i = 0; i < paramsCount; i++) { + score += glm::abs((params[i] - biome.parameters[i].origin) / + biome.parameters[i].weight); + } + if (score < chosenScore) { + chosenScore = score; + chosenBiome = &biome; + } + } + return chosenBiome; +} + #include "util/timeutil.hpp" void WorldGenerator::generate( voxel* voxels, int chunkX, int chunkZ, uint64_t seed @@ -55,11 +82,11 @@ void WorldGenerator::generate( auto heightmap = def.script->generateHeightmap( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed ); + auto biomeParams = def.script->generateParameterMaps( + {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed + ); auto values = heightmap->getValues(); const auto& biomes = def.script->getBiomes(); - const auto& biome = biomes.at(0); - const auto& groundLayers = biome.groundLayers; - const auto& seaLayers = biome.seaLayers; uint seaLevel = def.script->getSeaLevel(); @@ -67,9 +94,14 @@ void WorldGenerator::generate( for (uint z = 0; z < CHUNK_D; z++) { for (uint x = 0; x < CHUNK_W; x++) { + const Biome* biome = choose_biome(biomes, biomeParams, x, z); + int height = values[z * CHUNK_W + x] * CHUNK_H; height = std::max(0, height); + const auto& groundLayers = biome->groundLayers; + const auto& seaLayers = biome->seaLayers; + generate_pole(seaLayers, seaLevel, height, seaLevel, voxels, x, z); generate_pole(groundLayers, height, 0, seaLevel, voxels, x, z); }