diff options
| author | Mel <einebeere@gmail.com> | 2023-07-07 21:39:42 +0200 |
|---|---|---|
| committer | Mel <einebeere@gmail.com> | 2023-07-07 21:39:42 +0200 |
| commit | f1fc192ddc4c739fa8b4b376c759b7d3218a34eb (patch) | |
| tree | 9e9afb9a21ba3ca27d1f25d46230aa9d27f8be39 /src/World/Generation/Generator.cpp | |
| parent | 24b8124469350d1c80d0553cf3f4bf58cdb1489b (diff) | |
| download | meowcraft-f1fc192ddc4c739fa8b4b376c759b7d3218a34eb.tar.zst meowcraft-f1fc192ddc4c739fa8b4b376c759b7d3218a34eb.zip | |
Chunk-bound tree decoration
Diffstat (limited to 'src/World/Generation/Generator.cpp')
| -rw-r--r-- | src/World/Generation/Generator.cpp | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/src/World/Generation/Generator.cpp b/src/World/Generation/Generator.cpp new file mode 100644 index 0000000..af3c54d --- /dev/null +++ b/src/World/Generation/Generator.cpp @@ -0,0 +1,331 @@ +#include "Generator.hpp" +#include "../../Math/Interpolation.hpp" +#include "../../Math/Sigmoid.hpp" + +namespace MC::World::Generation { + +Chunk Generator::generate(int64_t chunk_x, int64_t chunk_y) { + Chunk chunk(chunk_x, chunk_y); + + auto landmass_map = generate_landmass_map(chunk_x, chunk_y); + auto hill_map = generate_hill_map(chunk_x, chunk_y); + auto height_map = generate_height_map(landmass_map, hill_map, chunk_x, chunk_y); + + auto temperature_map = generate_temperature_map(chunk_x, chunk_y); + auto humidity_map = generate_humidity_map(chunk_x, chunk_y); + auto biome_map = generate_biome_map(landmass_map, hill_map, temperature_map, humidity_map, chunk_x, chunk_y); + auto terrain_map = generate_terrain(height_map, chunk_x, chunk_y); + + decorate_soil(chunk, biome_map, terrain_map); + + chunk.set_details({landmass_map, hill_map, temperature_map, humidity_map, biome_map}); + + return chunk; +} + +#define SIMPLE_MAP_GENERATOR(name, getter) \ +Generator::Map2D<float> Generator::name(int64_t chunk_x, int64_t chunk_y) { \ + Matrix<Chunk::Width, Chunk::Width> map{}; \ + for (uint x = 0; x < Chunk::Width; x++) { \ + for (uint y = 0; y < Chunk::Width; y++) { \ + auto pos = chunk_position_to_world_vector(chunk_x, chunk_y, x, y); \ + map(x, y) = getter(pos); \ + } \ + } \ + return map; \ +} + +SIMPLE_MAP_GENERATOR(generate_landmass_map, get_landmass) +SIMPLE_MAP_GENERATOR(generate_hill_map, get_hill) + +Generator::Map2D<float> Generator::generate_height_map(Map2D<float>& landmass_map, Map2D<float>& hill_map, int64_t chunk_x, int64_t chunk_y) { + Map2D<float> height_map{}; + + for (uint x = 0; x < Chunk::Width; x++) { + for (uint y = 0; y < Chunk::Width; y++) { + auto landmass_effect = landmass_map(x, y); + auto hill_effect = hill_map(x, y); + + auto hill = hill_effect * landmass_effect * 1.4 - 0.4; + auto landmass = landmass_effect * 0.6 + 0.4; + + auto height = (hill + landmass) / 2.0f; + + height_map(x, y) = height; + } + } + + return height_map; +} + +SIMPLE_MAP_GENERATOR(generate_temperature_map, get_temperature) +SIMPLE_MAP_GENERATOR(generate_humidity_map, get_humidity) + +Generator::Map2D<BiomeType> Generator::generate_biome_map( + Map2D<float>& landmass_map, Map2D<float>& hill_map, + Map2D<float>& temperature_map, Map2D<float>& humidity_map, + int64_t chunk_x, int64_t chunk_y +) { + Map2D<BiomeType> biome_map{}; + + for (uint x = 0; x < Chunk::Width; x++) { + for (uint y = 0; y < Chunk::Width; y++) { + float landmass = landmass_map(x, y); + float hill = hill_map(x, y); + + float temperature = temperature_map(x, y); + float humidity = humidity_map(x, y); + + HillSlice hill_slice; + if (hill > 0.55) { hill_slice = HillSlice::Mountain; } + else if (hill > 0.03) { hill_slice = HillSlice::Middle; } + else { hill_slice = HillSlice::Valley; } + + LandmassSlice landmass_slice; + if (landmass > 0.8) { landmass_slice = LandmassSlice::Land; } + else if (landmass > 0.45) { landmass_slice = LandmassSlice::Beach; } + else { landmass_slice = LandmassSlice::Ocean; } + + TemperatureZone temparature_zone; + if (temperature > 0.6) { temparature_zone = TemperatureZone::Hot; } + else if (temperature > 0.4) { temparature_zone = TemperatureZone::Fair; } + else { temparature_zone = TemperatureZone::Cold; } + + HumidityZone humidity_zone; + if (humidity > 0.66) { humidity_zone = HumidityZone::Wet; } + else if (humidity > 0.33) { humidity_zone = HumidityZone::Temperate; } + else { humidity_zone = HumidityZone::Dry; } + + auto biome = lookup_biome(hill_slice, landmass_slice, temparature_zone, humidity_zone); + biome_map(x, y) = biome; + } + } + + return biome_map; +} + +Generator::Map3D<bool> Generator::generate_terrain(Map2D<float>& height_map, int64_t chunk_x, int64_t chunk_y) { + float jaggedness = 0.10f; + + Map3D<bool> terrain_map{}; + for (uint x = 0; x < Chunk::Width; x++) { + for (uint z = 0; z < Chunk::Width; z++) { + auto height = height_map(x, z); + + for (uint y = 0; y < Chunk::Height; y++) { + float density = get_density({Chunk::Width * chunk_x + (float)x, (float)y, Chunk::Width * chunk_y + (float)z}); + float threshold = Math::sigmoid(((float)y / (Chunk::Height * height * 2) - 0.5f) / jaggedness); + + if (density > threshold) { + terrain_map(x, y, z) = true; + } + } + } + } + + return terrain_map; +} + +void Generator::decorate_soil(Chunk& chunk, Map2D<BiomeType>& biome_map, Map3D<bool>& terrain_map) { + constexpr uint dirt_depth = 4; + constexpr uint water_height = 40; + + for (uint x = 0; x < Chunk::Width; x++) { + for (uint z = 0; z < Chunk::Width; z++) { + auto biome = biome_map(x, z); + + BlockType top_block{}; + BlockType soil_block{}; + switch (biome) { + case BiomeType::Beach: + case BiomeType::Desert: + top_block = BlockType::Sand; + soil_block = BlockType::Sand; + break; + case BiomeType::Jungle: + case BiomeType::Forest: + case BiomeType::Plains: + case BiomeType::Ocean: + case BiomeType::River: + top_block = BlockType::Grass; + soil_block = BlockType::Dirt; + break; + case BiomeType::Alpine: + top_block = BlockType::Snow; + soil_block = BlockType::Dirt; + break; + case BiomeType::Shore: + case BiomeType::RockyPeaks: + top_block = BlockType::Stone; + soil_block = BlockType::Stone; + } + + auto column_depth = 0; + for (uint y = Chunk::Height - 1; y > 0; y--) { + auto block = terrain_map(x, y, z); + if (block) { + if (column_depth == 0 && y >= water_height - 1) { + chunk.set(x, y, z, {top_block}); + } else if (column_depth < dirt_depth) { + chunk.set(x, y, z, {soil_block}); + } else { + chunk.set(x, y, z, {BlockType::Stone}); + } + column_depth++; + } else { + if (y < water_height) { + chunk.set(x, y, z, {BlockType::Water}); + } + column_depth = 0; + } + } + } + } + + for (auto& decorator : s_decorators) { + decorator->decorate_chunk(chunk); + } +} + +#define CURVE_START(y) constexpr auto lerp = Math::linear_interpolation; float _py = y; float _px = 0.0f; +#define CURVE_POINT(x, y) if (v < x) return lerp({_py, y}, _px, x, v); _py = y; _px = x +#define CURVE_END(y) return lerp({_py, y}, _px, 1.0f, v); + +float Generator::get_landmass(Vector<2> pos) const { + auto v = m_landmass_noise.at(pos); + + CURVE_START(1.0f); + CURVE_POINT(0.1f, 0.8f); + CURVE_POINT(0.3f, 0.8f); + CURVE_POINT(0.37f, 0.125f); + CURVE_POINT(0.4f, 0.4f); + CURVE_POINT(0.46f, 0.45f); + CURVE_POINT(0.47f, 0.875f); + CURVE_POINT(0.48f, 0.9f); + CURVE_POINT(0.55f, 0.95f); + CURVE_END(1.0f); +} + +float Generator::get_hill(Vector<2> pos) const { + auto v = m_hill_noise.at(pos); + + CURVE_START(0.33f); + CURVE_POINT(0.25f, 1.0f); + + CURVE_POINT(0.49f, 0.1f); + CURVE_POINT(0.5f, 0.0f); + CURVE_POINT(0.51f, 0.1f); + + CURVE_POINT(0.75f, 1.0f); + CURVE_END(0.33f); +} + +float Generator::get_humidity(Vector<2> pos) const { + auto v = m_humidity_noise.at(pos); + return v; +} + +float Generator::get_temperature(Vector<2> pos) const { + auto v = m_temperature_noise.at(pos); + return v; +} + +float Generator::get_density(Vector<3> pos) const { + auto v = m_density_noise.at(pos); + return v; +} + +Vector<2> Generator::chunk_position_to_world_vector(int64_t chunk_x, int64_t chunk_y, uint x, uint y) { + return {(float)x + chunk_x * Chunk::Width, (float)y + chunk_y * Chunk::Width}; +} + +std::array<BiomeType, Generator::biome_lookup_table_size> Generator::create_biome_lookup_table() { + std::array<BiomeType, biome_lookup_table_size> table{}; + + auto inc = [](auto& x) { x = (std::remove_reference_t<decltype(x)>)((uint)x + 1); }; + auto cmp = [](auto x, auto s) { return (uint)x < (uint)s; }; + + for (HillSlice hill_slice{}; cmp(hill_slice, HillSliceSize); inc(hill_slice)) { + for (LandmassSlice landmass_slice{}; cmp(landmass_slice, LandmassSliceSize); inc(landmass_slice)) { + for (TemperatureZone temperature_zone{}; cmp(temperature_zone, TemperatureZoneSize); inc(temperature_zone)) { + for (HumidityZone humidity_zone{}; cmp(humidity_zone, HumidityZoneSize); inc(humidity_zone)) { + BiomeType biome{}; + if (landmass_slice == LandmassSlice::Ocean) { + biome = BiomeType::Ocean; + goto set; + } + if (landmass_slice == LandmassSlice::Beach) { + if (temperature_zone == TemperatureZone::Cold) { + biome = BiomeType::Shore; + } else { + biome = BiomeType::Beach; + } + goto set; + } + + if (hill_slice == HillSlice::Valley) { + biome = BiomeType::River; + goto set; + } + if (hill_slice == HillSlice::Mountain) { + if (temperature_zone == TemperatureZone::Cold) { + biome = BiomeType::Alpine; + } else { + biome = BiomeType::RockyPeaks; + } + goto set; + } + + switch (temperature_zone) { + case TemperatureZone::Hot: + biome = BiomeType::Desert; + break; + case TemperatureZone::Fair: + switch (humidity_zone) { + case HumidityZone::Wet: + biome = BiomeType::Jungle; + break; + case HumidityZone::Lush: + biome = BiomeType::Forest; + break; + case HumidityZone::Temperate: + case HumidityZone::Dry: + biome = BiomeType::Plains; + break; + } + break; + case TemperatureZone::Cold: + switch (humidity_zone) { + case HumidityZone::Wet: + case HumidityZone::Lush: + biome = BiomeType::Alpine; + break; + case HumidityZone::Temperate: + case HumidityZone::Dry: + biome = BiomeType::Plains; + break; + } + break; + } + + set: + table[biome_lookup_table_index(hill_slice, landmass_slice, temperature_zone, humidity_zone)] = biome; + } + } + } + } + + return table; +} + +size_t Generator::biome_lookup_table_index(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone) { + auto hs = (uint8_t)hill_slice; auto ls = (uint8_t)landmass_slice; auto tz = (uint8_t)temperature_zone; auto hz = (uint8_t)humidity_zone; + auto LS_S = (uint8_t)LandmassSliceSize; auto TZ_S = (uint8_t)TemperatureZoneSize; auto HZ_S = (uint8_t)HumidityZoneSize; + return (hs * LS_S * TZ_S * HZ_S) + (ls * TZ_S * HZ_S) + (tz * HZ_S) + hz; +} + +BiomeType Generator::lookup_biome(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone) { + return biome_lookup_table.at(biome_lookup_table_index(hill_slice, landmass_slice, temperature_zone, humidity_zone)); +} + +} |
