summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2023-06-29 22:15:32 +0200
committerMel <einebeere@gmail.com>2023-06-29 22:15:32 +0200
commit52732d71e72b02ff45e25f44e414f87ec9ab7666 (patch)
treea3db775e01e04abaf0291e7d172740ff47ff1006 /src
parent92ac46df6afa8ee76f972cceb681cf32658f84a2 (diff)
downloadmeowcraft-52732d71e72b02ff45e25f44e414f87ec9ab7666.tar.zst
meowcraft-52732d71e72b02ff45e25f44e414f87ec9ab7666.zip
Pretty terrain generation
Diffstat (limited to 'src')
-rw-r--r--src/Math/Common.hpp3
-rw-r--r--src/Math/Constants.hpp8
-rw-r--r--src/Math/Perlin.hpp3
-rw-r--r--src/Math/Sigmoid.hpp13
-rw-r--r--src/Math/Trig.hpp4
-rw-r--r--src/World/BiomeType.hpp22
-rw-r--r--src/World/BlockType.hpp6
-rw-r--r--src/World/Chunk.cpp64
-rw-r--r--src/World/Chunk.hpp25
-rw-r--r--src/World/Generator.cpp312
-rw-r--r--src/World/Generator.hpp59
-rw-r--r--src/World/World.cpp12
-rw-r--r--src/World/World.hpp3
13 files changed, 469 insertions, 65 deletions
diff --git a/src/Math/Common.hpp b/src/Math/Common.hpp
index 94840b1..1ea75f2 100644
--- a/src/Math/Common.hpp
+++ b/src/Math/Common.hpp
@@ -3,4 +3,5 @@
#include "Vector.hpp"
#include "Matrix.hpp"
#include "Rotation.hpp"
-#include "Trig.hpp" \ No newline at end of file
+#include "Trig.hpp"
+#include "Constants.hpp" \ No newline at end of file
diff --git a/src/Math/Constants.hpp b/src/Math/Constants.hpp
new file mode 100644
index 0000000..9c62e94
--- /dev/null
+++ b/src/Math/Constants.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+namespace Math {
+
+constexpr double PI = 3.14159265358979323846;
+constexpr double E = 2.71828182845904523536;
+
+} \ No newline at end of file
diff --git a/src/Math/Perlin.hpp b/src/Math/Perlin.hpp
index c31697b..17d33fd 100644
--- a/src/Math/Perlin.hpp
+++ b/src/Math/Perlin.hpp
@@ -16,6 +16,7 @@ struct Noise {
uint octaves = 3;
float persistence = 0.3f;
+ float lacunarity = 2.0f;
float at(Vector<D> pos) const {
float result = 0;
@@ -27,7 +28,7 @@ struct Noise {
result += raw((pos + offset).abs() / scale * frequency) * amplitude;
max += amplitude;
- frequency *= 2;
+ frequency *= lacunarity;
amplitude *= persistence;
}
diff --git a/src/Math/Sigmoid.hpp b/src/Math/Sigmoid.hpp
new file mode 100644
index 0000000..f2fa009
--- /dev/null
+++ b/src/Math/Sigmoid.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <cmath>
+#include "Constants.hpp"
+
+namespace Math {
+
+template <typename T>
+T sigmoid(T x) {
+ return 1 / (1 + std::pow(E, -x));
+}
+
+}
diff --git a/src/Math/Trig.hpp b/src/Math/Trig.hpp
index c64e2f2..2a415f5 100644
--- a/src/Math/Trig.hpp
+++ b/src/Math/Trig.hpp
@@ -1,8 +1,8 @@
#pragma once
-namespace Math {
+#include "Constants.hpp"
-constexpr double PI = 3.14159265358979323846;
+namespace Math {
template<typename T>
T radians(T degrees) {
diff --git a/src/World/BiomeType.hpp b/src/World/BiomeType.hpp
index 026b3ef..2e3d8c6 100644
--- a/src/World/BiomeType.hpp
+++ b/src/World/BiomeType.hpp
@@ -8,34 +8,32 @@ namespace MC::World {
class BiomeType {
public:
enum Value : uint8_t {
- Forest,
Plains,
+ Forest,
+ Alpine,
Desert,
+ Jungle,
+ Beach,
+ River,
Ocean,
};
- static constexpr const size_t Size = 4;
+ static constexpr uint8_t Size = Ocean + 1;
- BiomeType() = default;
- BiomeType(Value biome) : m_biome(biome) {}
+ BiomeType() : m_biome(Plains) {}
+ BiomeType(const Value biome) : m_biome(biome) {}
operator Value() const { return m_biome; }
static std::vector<BiomeType> all() {
- return {
- Plains, Forest, Desert, Ocean
- };
+ return { Plains, Forest, Alpine, Desert, Jungle, Beach, River, Ocean };
}
static std::vector<BiomeType> all_ground() {
- return {
- Plains, Forest, Desert
- };
+ return { Plains, Forest, Alpine, Desert, Jungle, Beach };
}
private:
-
-
Value m_biome;
};
diff --git a/src/World/BlockType.hpp b/src/World/BlockType.hpp
index 65afe0d..dcb8e44 100644
--- a/src/World/BlockType.hpp
+++ b/src/World/BlockType.hpp
@@ -13,12 +13,13 @@ public:
Grass,
Stone,
Sand,
+ Snow,
Water,
};
- static constexpr const size_t Size = 6;
+ static constexpr uint8_t Size = Water + 1;
- BlockType() = default;
+ BlockType() : m_block(Air) {}
BlockType(Value block) : m_block(block) {}
operator Value() const { return m_block; }
@@ -30,6 +31,7 @@ public:
Grass,
Stone,
Sand,
+ Snow,
Water,
};
}
diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp
index 1874950..9642fa9 100644
--- a/src/World/Chunk.cpp
+++ b/src/World/Chunk.cpp
@@ -4,11 +4,11 @@
namespace MC::World {
void Chunk::set(uint32_t x, uint32_t y, uint32_t z, BlockData data) {
- m_blocks[pos(x, y, z)] = data;
+ m_blocks.at(pos(x, y, z)) = data;
}
-Chunk::BlockData Chunk::get(uint32_t x, uint32_t y, uint32_t z) {
- return m_blocks[pos(x, y, z)];
+Chunk::BlockData Chunk::get(uint32_t x, uint32_t y, uint32_t z) const {
+ return m_blocks.at(pos(x, y, z));
}
GFX::Mesh Chunk::mesh() {
@@ -17,9 +17,9 @@ GFX::Mesh Chunk::mesh() {
std::vector<Vector<2>> tex_coords{};
std::vector<uint32_t> indices{};
- for (int x = 0; x < Chunk::Width; x++) {
- for (int y = 0; y < Chunk::Height; y++) {
- for (int z = 0; z < Chunk::Width; z++) {
+ for (int x = 0; x < Width; x++) {
+ for (int y = 0; y < Height; y++) {
+ for (int z = 0; z < Width; z++) {
auto type = get(x, y, z).type;
if (type == BlockType::Air) {
continue;
@@ -31,8 +31,8 @@ GFX::Mesh Chunk::mesh() {
}
auto side_positions = side.face();
- auto side_normals = Chunk::face_normals(side);
- auto side_tex_coords = Chunk::face_tex_coords(type, side);
+ auto side_normals = face_normals(side);
+ auto side_tex_coords = face_tex_coords(type, side);
for (auto& position : side_positions) {
position = position + Vector<3>{static_cast<float>(x), static_cast<float>(y), static_cast<float>(z)};
@@ -87,9 +87,9 @@ bool Chunk::is_face_visible(uint32_t x, uint32_t y, uint32_t z, BlockSide side)
};
if (
- neighbor_pos.x() >= Chunk::Width || neighbor_pos.x() < 0 ||
- neighbor_pos.y() >= Chunk::Height || neighbor_pos.y() < 0 ||
- neighbor_pos.z() >= Chunk::Width || neighbor_pos.z() < 0
+ neighbor_pos.x() >= Width || neighbor_pos.x() < 0 ||
+ neighbor_pos.y() >= Height || neighbor_pos.y() < 0 ||
+ neighbor_pos.z() >= Width || neighbor_pos.z() < 0
) {
return true;
}
@@ -103,8 +103,8 @@ bool Chunk::is_face_visible(uint32_t x, uint32_t y, uint32_t z, BlockSide side)
}
std::array<Vector<2>, 4> Chunk::face_tex_coords(BlockType type, BlockSide side) {
- uint8_t atlas_width = 2;
- uint8_t atlas_height = 3;
+ uint8_t atlas_width = 4;
+ uint8_t atlas_height = 4;
float width_step = 1.0f / atlas_width;
float height_step = 1.0f / atlas_height;
@@ -125,22 +125,34 @@ std::array<Vector<2>, 4> Chunk::face_tex_coords(BlockType type, BlockSide side)
return block_coords(1, 0);
case BlockType::Grass:
switch (side) {
- case BlockSide::Front:
- case BlockSide::Back:
- case BlockSide::Left:
- case BlockSide::Right:
- return block_coords(0, 1);
- case BlockSide::Top:
- return block_coords(0, 0);
- case BlockSide::Bottom:
- return block_coords(1, 0);
+ case BlockSide::Front:
+ case BlockSide::Back:
+ case BlockSide::Left:
+ case BlockSide::Right:
+ return block_coords(2, 0);
+ case BlockSide::Bottom:
+ return block_coords(1, 0);
+ case BlockSide::Top:
+ return block_coords(0, 0);
}
case BlockType::Stone:
- return block_coords(1, 1);
+ return block_coords(3, 0);
case BlockType::Sand:
- return block_coords(0, 2);
+ return block_coords(0, 1);
case BlockType::Water:
- return block_coords(1, 2);
+ return block_coords(1, 1);
+ case BlockType::Snow:
+ switch (side) {
+ case BlockSide::Front:
+ case BlockSide::Back:
+ case BlockSide::Left:
+ case BlockSide::Right:
+ return block_coords(3, 1);
+ case BlockSide::Bottom:
+ return block_coords(1, 0);
+ case BlockSide::Top:
+ return block_coords(2, 1);
+ }
case BlockType::Air:
return {};
}
@@ -159,7 +171,7 @@ std::array<Vector<3>, 4> Chunk::face_normals(BlockSide side) {
}
uint64_t Chunk::pos(uint32_t x, uint32_t y, uint32_t z) {
- return x + Chunk::Width * y + Chunk::Width * Chunk::Height * z;
+ return x + Width * y + Width * Height * z;
}
}
diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp
index 2b0379c..d200039 100644
--- a/src/World/Chunk.hpp
+++ b/src/World/Chunk.hpp
@@ -1,29 +1,36 @@
#pragma once
#include <cstdint>
-#include <optional>
+#include "BiomeType.hpp"
#include "BlockType.hpp"
#include "../GFX/Mesh.hpp"
#include "BlockSide.hpp"
-#include "../GFX/Binder.hpp"
namespace MC::World {
class Chunk {
public:
- static constexpr const uint32_t Width = 16;
- static constexpr const uint32_t Height = 128;
+ static constexpr uint32_t Width = 16;
+ static constexpr uint32_t Height = 128;
Chunk(int64_t x, int64_t y)
- : m_blocks{Chunk::Width * Chunk::Height * Chunk::Width, {BlockType::Air}},
- m_position{(float)x * Chunk::Width, 0.0f, (float)y * Chunk::Width} {};
+ : m_blocks{Width * Height * Width, {BlockType::Air}},
+ m_position{(float)x * Width, 0.0f, (float)y * Width} {}
struct BlockData {
BlockType type;
};
- void set(uint32_t x, uint32_t y, uint32_t z, BlockData type);
- BlockData get(uint32_t x, uint32_t y, uint32_t z);
+ void set(uint32_t x, uint32_t y, uint32_t z, BlockData data);
+ BlockData get(uint32_t x, uint32_t y, uint32_t z) const;
+
+ struct Details {
+ Matrix<Width, Width> landmass_values{};
+ Matrix<Width, Width> hill_values{};
+ Matrix<Width, Width, BiomeType> biome_values{};
+ };
+ void set_details(const Details& details) { m_details = details; }
+ Details& details(){ return m_details; }
Vector<3> position();
GFX::Mesh mesh();
@@ -37,6 +44,8 @@ private:
Vector<3> m_position;
std::vector<BlockData> m_blocks;
+
+ Details m_details;
};
}
diff --git a/src/World/Generator.cpp b/src/World/Generator.cpp
index e8b3bbd..f528278 100644
--- a/src/World/Generator.cpp
+++ b/src/World/Generator.cpp
@@ -1,30 +1,320 @@
#include "Generator.hpp"
-#include "../Math/Perlin.hpp"
+#include "../Math/Interpolation.hpp"
+#include "../Math/Sigmoid.hpp"
namespace MC::World {
Chunk Generator::generate(int64_t chunk_x, int64_t chunk_y) {
Chunk chunk(chunk_x, chunk_y);
- Math::Perlin::Noise<3> noise{.scale=20.0f, .octaves=2};
+ 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 biome_map = generate_biome_map(landmass_map, hill_map, height_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.map}, {hill_map.map}, {biome_map.map}});
+
+ return chunk;
+}
+
+Generator::ChunkMap2D<float> Generator::generate_landmass_map(int64_t chunk_x, int64_t chunk_y) {
+ ChunkMap2D<float> landmass_map{};
+
+ for (uint x = 0; x < Chunk::Width; x++) {
+ for (uint y = 0; y < Chunk::Width; y++) {
+ landmass_map.set(x, y, get_landmass(chunk_position_to_world_vector(chunk_x, chunk_y, x, y)));
+ }
+ }
+
+ return landmass_map;
+}
+
+Generator::ChunkMap2D<float> Generator::generate_hill_map(int64_t chunk_x, int64_t chunk_y) {
+ ChunkMap2D<float> hill_map{};
+
+ for (uint x = 0; x < Chunk::Width; x++) {
+ for (uint y = 0; y < Chunk::Width; y++) {
+ hill_map.set(x, y, get_hill(chunk_position_to_world_vector(chunk_x, chunk_y, x, y)));
+ }
+ }
+
+ return hill_map;
+}
+
+Generator::ChunkMap2D<float> Generator::generate_height_map(ChunkMap2D<float>& landmass_map, ChunkMap2D<float>& hill_map, int64_t chunk_x, int64_t chunk_y) {
+ ChunkMap2D<float> height_map{};
+
+ for (uint x = 0; x < Chunk::Width; x++) {
+ for (uint y = 0; y < Chunk::Width; y++) {
+ auto landmass_effect = landmass_map.get(x, y);
+ auto hill_effect = hill_map.get(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.set(x, y, height);
+ }
+ }
+
+ return height_map;
+}
+
+Generator::ChunkMap2D<BiomeType> Generator::generate_biome_map(ChunkMap2D<float>& landmass_map, ChunkMap2D<float>& hill_map, ChunkMap2D<float>& height_map, int64_t chunk_x, int64_t chunk_y) {
+ ChunkMap2D<BiomeType> biome_map{};
for (uint x = 0; x < Chunk::Width; x++) {
+ for (uint y = 0; y < Chunk::Width; y++) {
+ float landmass = landmass_map.get(x, y);
+ float hill = hill_map.get(x, y);
+
+ auto world_pos = chunk_position_to_world_vector(chunk_x, chunk_y, x, y);
+ float temperature = get_temperature(world_pos);
+ float humidity = get_humidity(world_pos);
+
+ HillSlice hill_slice;
+ if (hill > 0.9) { hill_slice = HillSlice::Mountain; }
+ else if (hill > 0.33) { 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.66) { temparature_zone = TemperatureZone::Hot; }
+ else if (temperature > 0.33) { 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.set(x, y, biome);
+ }
+ }
+
+ return biome_map;
+}
+
+Generator::ChunkMap3D<bool> Generator::generate_terrain(ChunkMap2D<float>& height_map, int64_t chunk_x, int64_t chunk_y) {
+ float jaggedness = 0.10f;
+
+ ChunkMap3D<bool> terrain_map{};
+ for (uint x = 0; x < Chunk::Width; x++) {
for (uint z = 0; z < Chunk::Width; z++) {
+ auto height = height_map.get(x, z);
+
for (uint y = 0; y < Chunk::Height; y++) {
- auto value = noise.at({
- (float)(chunk_x * Chunk::Width + x),
- (float)y,
- (float)(chunk_y * Chunk::Width + z),
- });
+ 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 (value > 0.6f && value < 0.7f) {
- chunk.set(x, y, z, {BlockType::Stone});
+ if (density > threshold) {
+ terrain_map.set(x, y, z, true);
}
}
}
}
- return chunk;
+ return terrain_map;
}
-} \ No newline at end of file
+void Generator::decorate_soil(Chunk& chunk, ChunkMap2D<BiomeType>& biome_map, ChunkMap3D<bool>& terrain_map) {
+ constexpr uint dirt_depth = 4;
+ constexpr uint water_height = 39;
+
+ for (uint x = 0; x < Chunk::Width; x++) {
+ for (uint z = 0; z < Chunk::Width; z++) {
+ auto biome = biome_map.get(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::River:
+ case BiomeType::Ocean:
+ top_block = BlockType::Grass;
+ soil_block = BlockType::Dirt;
+ break;
+ case BiomeType::Alpine:
+ top_block = BlockType::Snow;
+ soil_block = BlockType::Dirt;
+ break;
+ }
+
+ auto column_depth = 0;
+ for (uint y = Chunk::Height - 1; y > 0; y--) {
+ auto block = terrain_map.get(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;
+ }
+ }
+ }
+ }
+}
+
+#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) {
+ biome = BiomeType::Beach;
+ goto set;
+ }
+
+ if (hill_slice == HillSlice::Valley) {
+ biome = BiomeType::River;
+ goto set;
+ }
+ if (hill_slice == HillSlice::Mountain) {
+ if (temperature_zone == TemperatureZone::Hot) {
+ biome = BiomeType::Desert;
+ } else {
+ biome = BiomeType::Alpine;
+ }
+ 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));
+}
+
+}
diff --git a/src/World/Generator.hpp b/src/World/Generator.hpp
index afe43c6..523136a 100644
--- a/src/World/Generator.hpp
+++ b/src/World/Generator.hpp
@@ -3,6 +3,7 @@
#include <cstdint>
#include "Chunk.hpp"
#include "BiomeType.hpp"
+#include "../Math/Perlin.hpp"
namespace MC::World {
@@ -10,6 +11,64 @@ class Generator {
public:
Generator() = default;
Chunk generate(int64_t chunk_x, int64_t chunk_y);
+private:
+ template <typename V>
+ struct ChunkMap2D {
+ V map[Chunk::Width * Chunk::Width];
+ void set(uint x, uint y, V v) { map[x * Chunk::Width + y] = v; }
+ V get(uint x, uint y) { return map[x * Chunk::Width + y]; }
+ };
+
+ template <typename V>
+ struct ChunkMap3D {
+ V map[Chunk::Width * Chunk::Width * Chunk::Height];
+ void set(uint x, uint y, uint z, V v) { map[pos(x, y, z)] = v; }
+ V get(uint x, uint y, uint z) { return map[pos(x, y, z)]; }
+ static uint pos(uint x, uint y, uint z) { return x + Chunk::Width * y + Chunk::Width * Chunk::Height * z; }
+ };
+
+ ChunkMap2D<float> generate_landmass_map(int64_t chunk_x, int64_t chunk_y);
+ ChunkMap2D<float> generate_hill_map(int64_t chunk_x, int64_t chunk_y);
+ ChunkMap2D<float> generate_height_map(ChunkMap2D<float>& landmass_map, ChunkMap2D<float>& hill_map, int64_t chunk_x, int64_t chunk_y);
+
+ ChunkMap2D<BiomeType> generate_biome_map(ChunkMap2D<float>& landmass_map, ChunkMap2D<float>& hill_map, ChunkMap2D<float>& height_map, int64_t chunk_x, int64_t chunk_y);
+
+ ChunkMap3D<bool> generate_terrain(ChunkMap2D<float>& height_map, int64_t chunk_x, int64_t chunk_y);
+
+ void decorate_soil(Chunk& chunk, ChunkMap2D<BiomeType>& biome_map, ChunkMap3D<bool>& terrain_map);
+
+ [[nodiscard]] float get_landmass(Vector<2> pos) const;
+ [[nodiscard]] float get_hill(Vector<2> pos) const;
+
+ [[nodiscard]] float get_humidity(Vector<2> pos) const;
+ [[nodiscard]] float get_temperature(Vector<2> pos) const;
+
+ [[nodiscard]] float get_density(Vector<3> pos) const;
+
+ static Vector<2> chunk_position_to_world_vector(int64_t chunk_x, int64_t chunk_y, uint x, uint y);
+
+ Math::Perlin::Noise<2> m_landmass_noise{.scale=800.0f, .octaves=4, .persistence=0.3f, .lacunarity=3.5f};
+ Math::Perlin::Noise<2> m_hill_noise{.scale=400.0f, .octaves=3, .persistence=0.5f, .lacunarity=2.0f};
+
+ Math::Perlin::Noise<2> m_temperature_noise{.scale=700.0f, .octaves=3, .persistence=0.5f, .lacunarity=2.0f};
+ Math::Perlin::Noise<2> m_humidity_noise{.scale=400.0f, .octaves=2, .persistence=0.5f, .lacunarity=2.0f};
+
+ Math::Perlin::Noise<3> m_density_noise{.scale=30.0f, .octaves=2, .persistence=0.7f, .lacunarity=2.0f};
+
+ enum class HillSlice { Mountain, Middle, Valley };
+ enum class LandmassSlice { Land, Beach, Ocean };
+ enum class TemperatureZone { Hot, Fair, Cold };
+ enum class HumidityZone { Wet, Lush, Temperate, Dry };
+ static constexpr uint HillSliceSize = (uint)HillSlice::Valley + 1;
+ static constexpr uint LandmassSliceSize = (uint)LandmassSlice::Ocean + 1;
+ static constexpr uint TemperatureZoneSize = (uint)TemperatureZone::Cold + 1;
+ static constexpr uint HumidityZoneSize = (uint)HumidityZone::Dry + 1;
+
+ static constexpr size_t biome_lookup_table_size = (size_t)HillSliceSize * (size_t)LandmassSliceSize * (size_t)TemperatureZoneSize * (size_t)HumidityZoneSize;
+ static std::array<BiomeType, biome_lookup_table_size> create_biome_lookup_table();
+ static size_t biome_lookup_table_index(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone);
+ static BiomeType lookup_biome(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone);
+ static inline std::array<BiomeType, biome_lookup_table_size> biome_lookup_table = create_biome_lookup_table();
};
}
diff --git a/src/World/World.cpp b/src/World/World.cpp
index 9d88af3..03ce60e 100644
--- a/src/World/World.cpp
+++ b/src/World/World.cpp
@@ -19,7 +19,7 @@ std::vector<World::ChunkData> World::get_visible_chunks(Vector<3> position) {
m_visible_chunks = visible_chunks;
}
- std::vector<World::ChunkData> chunks{};
+ std::vector<ChunkData> chunks{};
chunks.reserve(visible_chunks.size());
for (auto index : visible_chunks) {
auto& data = get(index);
@@ -31,6 +31,16 @@ std::vector<World::ChunkData> World::get_visible_chunks(Vector<3> position) {
return chunks;
}
+Chunk* World::get_chunk_for_positon(Vector<3> position) {
+ int32_t x = std::round(position.x() / Chunk::Width);
+ int32_t y = std::round(position.z() / Chunk::Width);
+ auto& data = get({x, y});
+ if (data.chunk.has_value()) {
+ return &data.chunk.value();
+ }
+ return nullptr;
+}
+
void World::process_chunk_visibility_updates(std::unordered_set<ChunkIndex>& new_chunks, Vector<3> player) {
for (auto new_index: new_chunks) {
auto& data = get(new_index);
diff --git a/src/World/World.hpp b/src/World/World.hpp
index d842600..a5f73ec 100644
--- a/src/World/World.hpp
+++ b/src/World/World.hpp
@@ -3,9 +3,9 @@
#include <memory>
#include <unordered_map>
#include <unordered_set>
-#include <utility>
#include "Generator.hpp"
#include "ChunkIndex.hpp"
+#include "../GFX/Binder.hpp"
#include "../Compute/Queue.hpp"
namespace MC::World {
@@ -28,6 +28,7 @@ public:
};
std::vector<ChunkData> get_visible_chunks(Vector<3> position);
+ Chunk* get_chunk_for_positon(Vector<3> position);
private:
std::unordered_set<ChunkIndex> get_visible_chunk_indices(Vector<3> position) const;
std::unordered_set<ChunkIndex> load_finished_chunks_from_queue();