add plants
This commit is contained in:
parent
6efc942a1d
commit
5bdabaea42
@ -18,6 +18,10 @@ biomes = {
|
||||
{block="base:dirt", height=5, below_sea_level=false},
|
||||
{block="base:stone", height=-1},
|
||||
{block="base:bazalt", height=1},
|
||||
},
|
||||
plant_chance = 0.5,
|
||||
plants = {
|
||||
{block="base:grass", weight=1}
|
||||
}
|
||||
},
|
||||
desert = {
|
||||
|
||||
@ -610,6 +610,17 @@ namespace lua {
|
||||
return def;
|
||||
}
|
||||
|
||||
inline Number get_number_field(
|
||||
lua::State* L, const std::string& name, Number def, int idx=-1
|
||||
) {
|
||||
if (getfield(L, name, idx)) {
|
||||
auto value = tonumber(L, -1);
|
||||
pop(L);
|
||||
return value;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
inline Integer get_integer_field(
|
||||
lua::State* L, const std::string& name,
|
||||
Integer def, Integer min, Integer max, int idx=-1
|
||||
|
||||
@ -83,6 +83,9 @@ public:
|
||||
for (auto& layer : biome.seaLayers.layers) {
|
||||
layer.rt.id = content->blocks.require(layer.block).rt.id;
|
||||
}
|
||||
for (auto& plant : biome.plants.plants) {
|
||||
plant.rt.id = content->blocks.require(plant.block).rt.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,18 +166,37 @@ static inline Biome load_biome(
|
||||
}
|
||||
lua::pop(L);
|
||||
|
||||
BlocksLayers groundLayers;
|
||||
BlocksLayers seaLayers;
|
||||
try {
|
||||
groundLayers = load_layers(L, "layers");
|
||||
seaLayers = load_layers(L, "sea_layers");
|
||||
} catch (const std::runtime_error& err) {
|
||||
throw std::runtime_error("biome "+name+": "+err.what());
|
||||
float plantChance = lua::get_number_field(L, "plant_chance", 0.0);
|
||||
float plantsWeightSum = 0.0f;
|
||||
std::vector<PlantEntry> plants;
|
||||
if (lua::getfield(L, "plants")) {
|
||||
if (!lua::istable(L, -1)) {
|
||||
throw std::runtime_error("'plants' must be a table");
|
||||
}
|
||||
int plantsCount = lua::objlen(L, -1);
|
||||
for (int i = 1; i <= plantsCount; i++) {
|
||||
lua::rawgeti(L, i);
|
||||
if (!lua::istable(L, -1)) {
|
||||
throw std::runtime_error("plant must be a table");
|
||||
}
|
||||
auto block = lua::require_string_field(L, "block");
|
||||
float weight = lua::require_number_field(L, "weight");
|
||||
// TODO: range check (positive)
|
||||
plantsWeightSum += weight;
|
||||
plants.push_back(PlantEntry {block, weight, {}});
|
||||
lua::pop(L);
|
||||
}
|
||||
lua::pop(L);
|
||||
}
|
||||
// TODO: sort by weight descending
|
||||
|
||||
BlocksLayers groundLayers = load_layers(L, "layers");
|
||||
BlocksLayers seaLayers = load_layers(L, "sea_layers");
|
||||
lua::pop(L);
|
||||
return Biome {
|
||||
name,
|
||||
std::move(parameters),
|
||||
BiomePlants {plants, plantsWeightSum, plantChance},
|
||||
std::move(groundLayers),
|
||||
std::move(seaLayers)};
|
||||
}
|
||||
|
||||
@ -35,15 +35,56 @@ struct BlocksLayers {
|
||||
|
||||
struct BiomeParameter {
|
||||
/// @brief Central parameter value for the biome
|
||||
float origin;
|
||||
float value;
|
||||
/// @brief Parameter score multiplier
|
||||
/// (the higher the value, the greater the chance of biome choosing)
|
||||
/// (the higher the weight, the greater the chance of biome choosing)
|
||||
float weight;
|
||||
};
|
||||
|
||||
struct PlantEntry {
|
||||
/// @brief Plant block id
|
||||
std::string block;
|
||||
/// @brief Plant weight
|
||||
float weight;
|
||||
|
||||
struct {
|
||||
blockid_t id;
|
||||
} rt;
|
||||
};
|
||||
|
||||
struct BiomePlants {
|
||||
static inline float MIN_CHANCE = 0.000001f;
|
||||
|
||||
/// @brief Plant entries sorted by weight descending.
|
||||
std::vector<PlantEntry> plants;
|
||||
/// @brief Sum of weight values
|
||||
float weightsSum;
|
||||
/// @brief Plant generation chance
|
||||
float chance;
|
||||
|
||||
/// @brief Choose plant based on weight
|
||||
/// @param rand some random value in range [0, 1)
|
||||
/// @return index of chosen plant block
|
||||
inline blockid_t choose(float rand) const {
|
||||
if (plants.empty() || rand > chance || chance < MIN_CHANCE) {
|
||||
return 0;
|
||||
}
|
||||
rand = rand / chance;
|
||||
rand *= weightsSum;
|
||||
for (const auto& plant : plants) {
|
||||
rand -= plant.weight;
|
||||
if (rand <= 0.0f) {
|
||||
return plant.rt.id;
|
||||
}
|
||||
}
|
||||
return plants[plants.size()-1].rt.id;
|
||||
}
|
||||
};
|
||||
|
||||
struct Biome {
|
||||
std::string name;
|
||||
std::vector<BiomeParameter> parameters;
|
||||
BiomePlants plants;
|
||||
BlocksLayers groundLayers;
|
||||
BlocksLayers seaLayers;
|
||||
};
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "maths/util.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
#include "voxels/Chunk.hpp"
|
||||
@ -63,7 +64,7 @@ static inline const Biome* choose_biome(
|
||||
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) /
|
||||
score += glm::abs((params[i] - biome.parameters[i].value) /
|
||||
biome.parameters[i].weight);
|
||||
}
|
||||
if (score < chosenScore) {
|
||||
@ -92,6 +93,9 @@ void WorldGenerator::generate(
|
||||
|
||||
std::memset(voxels, 0, sizeof(voxel) * CHUNK_VOL);
|
||||
|
||||
PseudoRandom plantsRand;
|
||||
plantsRand.setSeed(chunkX, chunkZ);
|
||||
|
||||
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);
|
||||
@ -104,6 +108,15 @@ void WorldGenerator::generate(
|
||||
|
||||
generate_pole(seaLayers, seaLevel, height, seaLevel, voxels, x, z);
|
||||
generate_pole(groundLayers, height, 0, seaLevel, voxels, x, z);
|
||||
|
||||
if (height+1 > seaLevel) {
|
||||
// TODO: add underwater plants support
|
||||
float rand = (plantsRand.randU32() % RAND_MAX) / static_cast<float>(RAND_MAX);
|
||||
blockid_t plant = biome->plants.choose(rand);
|
||||
if (plant) {
|
||||
voxels[vox_index(x, height+1, z)].id = plant;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user