summary refs log tree commit diff
path: root/src/World
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2023-06-12 17:09:55 +0200
committerMel <einebeere@gmail.com>2023-06-12 17:14:03 +0200
commitd0de60dc33df75fbcacb53a09568b14d0fd48cb9 (patch)
tree7aefdbb81f114552881834bd5b0d842bc2bdb691 /src/World
parent23b0bc4d1ddc9fad3c32e8257497ddd13ac6a155 (diff)
downloadmeowcraft-d0de60dc33df75fbcacb53a09568b14d0fd48cb9.tar.zst
meowcraft-d0de60dc33df75fbcacb53a09568b14d0fd48cb9.zip
Multithreaded world generation with Perlin
Diffstat (limited to 'src/World')
-rw-r--r--src/World/Chunk.cpp16
-rw-r--r--src/World/Chunk.hpp18
-rw-r--r--src/World/ChunkIndex.hpp1
-rw-r--r--src/World/Generator.cpp184
-rw-r--r--src/World/Generator.hpp17
-rw-r--r--src/World/World.cpp69
-rw-r--r--src/World/World.hpp27
7 files changed, 108 insertions, 224 deletions
diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp
index 08f08bc..1874950 100644
--- a/src/World/Chunk.cpp
+++ b/src/World/Chunk.cpp
@@ -3,8 +3,12 @@
 
 namespace MC::World {
 
-void Chunk::set(uint32_t x, uint32_t y, uint32_t z, BlockType type) {
-    m_blocks[x][y][z].type = type;
+void Chunk::set(uint32_t x, uint32_t y, uint32_t z, BlockData data) {
+    m_blocks[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)];
 }
 
 GFX::Mesh Chunk::mesh() {
@@ -16,7 +20,7 @@ GFX::Mesh Chunk::mesh() {
     for (int x = 0; x < Chunk::Width; x++) {
         for (int y = 0; y < Chunk::Height; y++) {
             for (int z = 0; z < Chunk::Width; z++) {
-                auto type = m_blocks[x][y][z].type;
+                auto type = get(x, y, z).type;
                 if (type == BlockType::Air) {
                     continue;
                 }
@@ -90,7 +94,7 @@ bool Chunk::is_face_visible(uint32_t x, uint32_t y, uint32_t z, BlockSide side)
         return true;
     }
 
-    auto neighbor = m_blocks[neighbor_pos.x()][neighbor_pos.y()][neighbor_pos.z()];
+    auto neighbor = get(neighbor_pos.x(), neighbor_pos.y(), neighbor_pos.z());
     if (neighbor.type == BlockType::Air) {
         return true;
     }
@@ -154,4 +158,8 @@ std::array<Vector<3>, 4> Chunk::face_normals(BlockSide side) {
     return {normal, normal, normal, normal};
 }
 
+uint64_t Chunk::pos(uint32_t x, uint32_t y, uint32_t z) {
+    return x + Chunk::Width * y + Chunk::Width * Chunk::Height * z;
+}
+
 }
diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp
index cb4f7e1..2b0379c 100644
--- a/src/World/Chunk.hpp
+++ b/src/World/Chunk.hpp
@@ -12,12 +12,18 @@ namespace MC::World {
 class Chunk {
 public:
     static constexpr const uint32_t Width = 16;
-    static constexpr const uint32_t Height = 64;
+    static constexpr const uint32_t Height = 128;
 
     Chunk(int64_t x, int64_t y)
-        : m_blocks{}, m_position{(float)x * Chunk::Width, 0.0f, (float)y * Chunk::Width} {};
+        : m_blocks{Chunk::Width * Chunk::Height * Chunk::Width, {BlockType::Air}},
+        m_position{(float)x * Chunk::Width, 0.0f, (float)y * Chunk::Width} {};
 
-    void set(uint32_t x, uint32_t y, uint32_t z, BlockType type);
+    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);
 
     Vector<3> position();
     GFX::Mesh mesh();
@@ -27,12 +33,10 @@ private:
     static std::array<Vector<2>, 4> face_tex_coords(BlockType type, BlockSide side);
     static std::array<Vector<3>, 4> face_normals(BlockSide side);
 
-    struct BlockData {
-        BlockType type;
-    };
+    static uint64_t pos(uint32_t x, uint32_t y, uint32_t z);
 
     Vector<3> m_position;
-    BlockData m_blocks[Chunk::Width][Chunk::Height][Chunk::Width];
+    std::vector<BlockData> m_blocks;
 };
 
 }
diff --git a/src/World/ChunkIndex.hpp b/src/World/ChunkIndex.hpp
index cf5af07..bdb49b3 100644
--- a/src/World/ChunkIndex.hpp
+++ b/src/World/ChunkIndex.hpp
@@ -7,6 +7,7 @@
 namespace MC::World {
 
 struct ChunkIndex {
+    ChunkIndex() : x(0), y(0) {}
     ChunkIndex(int32_t x, int32_t y) : x(x), y(y) {}
 
     int32_t x, y;
diff --git a/src/World/Generator.cpp b/src/World/Generator.cpp
index 875a79b..e8b3bbd 100644
--- a/src/World/Generator.cpp
+++ b/src/World/Generator.cpp
@@ -1,190 +1,30 @@
 #include "Generator.hpp"
-#include "../Math/Noise.hpp"
+#include "../Math/Perlin.hpp"
 
 namespace MC::World {
 
 Chunk Generator::generate(int64_t chunk_x, int64_t chunk_y) {
     Chunk chunk(chunk_x, chunk_y);
 
-    auto ocean_weights = ocean_weights_pass(chunk_x, chunk_y);
-    auto biome_weights = biome_weights_pass(chunk_x, chunk_y, ocean_weights);
-    auto heights = height_pass(chunk_x, chunk_y, biome_weights);
-    auto biomes = flat_biome_pass(biome_weights);
+    Math::Perlin::Noise<3> noise{.scale=20.0f, .octaves=2};
 
-    float extent = 60.0f;
-    float base = 4.0f;
-
-    for (int x = 0; x < Chunk::Width; x++) {
-        for (int z = 0; z < Chunk::Width; z++) {
-            uint32_t height = std::round(heights(x, z) * extent + base);
-
-            auto biome = biomes(x, z);
-
-            BlockType top_block{};
-            BlockType fill_block{};
-            switch (biome) {
-                case BiomeType::Forest:
-                case BiomeType::Plains:
-                    top_block = BlockType::Grass;
-                    fill_block = BlockType::Dirt;
-                    break;
-                case BiomeType::Desert:
-                    top_block = BlockType::Sand;
-                    fill_block = BlockType::Sand;
-                    break;
-                case BiomeType::Ocean:
-                    top_block = BlockType::Water;
-                    fill_block = BlockType::Dirt;
-                    break;
-            }
-
-            for (int y = 0; y < Chunk::Height; y++) {
-                BlockType type = BlockType::Air;
-                if (y < (int32_t)height - 10) {
-                    type = BlockType::Stone;
-                } else if (y < height) {
-                    type = fill_block;
-                } else if (y == height) {
-                    type = top_block;
-                }
-
-                chunk.set(x, y, z, type);
-            }
-        }
-    }
-
-    return chunk;
-}
-
-Matrix<Chunk::Width, Chunk::Width> Generator::ocean_weights_pass(int64_t chunk_x, int64_t chunk_y) {
-    auto ocean_offset = 1125.0f;
-    auto ocean_scale = 0.05f;
-
-    Matrix<Chunk::Width, Chunk::Width> ocean_weights{};
-    for (int x = 0; x < Chunk::Width; x++) {
-        for (int y = 0; y < Chunk::Width; y++) {
-            float ocean_weight = Math::noise2d({
-                (chunk_x * Chunk::Width + x) * ocean_scale + ocean_offset,
-                (chunk_y * Chunk::Width + y) * ocean_scale + ocean_offset
-            });
-
-            ocean_weights(x, y) = ocean_weight;
-        }
-    }
-
-    return ocean_weights;
-}
-
-Matrix<Chunk::Width, Chunk::Width, Vector<BiomeType::Size>> Generator::biome_weights_pass(
-    int64_t chunk_x,
-    int64_t chunk_y,
-    Matrix<Chunk::Width, Chunk::Width, float> ocean_weights
-) {
-    auto ocean_threshold = 0.4f;
-
-    std::vector<float> biome_offsets = {110.0f, 2450.0f, 5042.0f};
-    auto biome_scale = 0.15f;
-
-    Matrix<Chunk::Width, Chunk::Width, Vector<BiomeType::Size>> biome_weights{};
-    for (int x = 0; x < Chunk::Width; x++) {
-        for (int y = 0; y < Chunk::Width; y++) {
-            Vector<BiomeType::Size> weights{};
-            for (auto biome : MC::World::BiomeType::all_ground()) {
-                float biome_noise = Math::noise2d({
-                    (chunk_x * Chunk::Width + x) * biome_scale + biome_offsets[biome],
-                    (chunk_y * Chunk::Width + y) * biome_scale + biome_offsets[biome]
+    for (uint x = 0; x < Chunk::Width; x++) {
+        for (uint z = 0; z < Chunk::Width; 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),
                 });
 
-                weights[biome] = biome_noise;
-            }
-
-            bool ocean_weight = ocean_weights(x, y) < ocean_threshold;
-            weights[MC::World::BiomeType::Ocean] = ocean_weight;
-
-            biome_weights(x,y) = weights;
-        }
-    }
-
-    return biome_weights;
-}
-
-Matrix<Chunk::Width, Chunk::Width> Generator::height_pass(
-    int64_t chunk_x,
-    int64_t chunk_y,
-    Matrix<Chunk::Width, Chunk::Width, Vector<BiomeType::Size>> biome_weights
-) {
-    auto height_offset = 6050.0f;
-    auto height_scale = 0.7f;
-
-    Matrix<Chunk::Width, Chunk::Width> heights{};
-    for (int x = 0; x < Chunk::Width; x++) {
-        for (int y = 0; y < Chunk::Width; y++) {
-            auto weights = biome_weights(x, y);
-
-            auto total_weight = 0.0f;
-            for (auto biome : MC::World::BiomeType::all()) {
-                total_weight += weights[biome];
-            }
-
-            std::pair<float, float> total_effect{};
-            for (auto biome : MC::World::BiomeType::all()) {
-                auto weight = weights[biome];
-
-                std::pair<float, float> effect{};
-                switch (biome) {
-                    case MC::World::BiomeType::Forest:
-                        effect = {0.5f, 0.45f};
-                        break;
-                    case MC::World::BiomeType::Plains:
-                        effect = {0.4f, 0.4f};
-                        break;
-                    case MC::World::BiomeType::Desert:
-                        effect = {0.6f, 0.35f};
-                        break;
-                    case MC::World::BiomeType::Ocean:
-                        effect = {0.0f, 0.0f};
-                        break;
-                }
-
-                total_effect = {
-                    total_effect.first + effect.first * (weight / total_weight),
-                    total_effect.second + effect.second * (weight / total_weight),
-                };
-            }
-
-            float height = Math::noise2d({
-                (chunk_x * Chunk::Width + x) * height_scale + height_offset,
-                (chunk_y * Chunk::Width + y) * height_scale + height_offset
-            }) * total_effect.first + total_effect.second;
-
-            heights(x, y) = height;
-        }
-    }
-
-    return heights;
-}
-
-Matrix<Chunk::Width, Chunk::Width, BiomeType> Generator::flat_biome_pass(
-    Matrix<Chunk::Width, Chunk::Width, Vector<BiomeType::Size>> biome_weights
-) {
-    Matrix<Chunk::Width, Chunk::Width, BiomeType>  biomes{};
-    for (int x = 0; x < Chunk::Width; x++) {
-        for (int y = 0; y < Chunk::Width; y++) {
-            auto weights = biome_weights(x, y);
-
-            std::pair<MC::World::BiomeType, float> max_biome{MC::World::BiomeType::Plains, 0.0f};
-            for (auto biome : MC::World::BiomeType::all()) {
-                auto weight = weights[biome];
-                if (weight > max_biome.second) {
-                    max_biome = {biome, weight};
+                if (value > 0.6f && value < 0.7f) {
+                    chunk.set(x, y, z, {BlockType::Stone});
                 }
             }
-
-            biomes(x, y) = max_biome.first;
         }
     }
 
-    return biomes;
+    return chunk;
 }
 
 }
\ No newline at end of file
diff --git a/src/World/Generator.hpp b/src/World/Generator.hpp
index f184ff9..afe43c6 100644
--- a/src/World/Generator.hpp
+++ b/src/World/Generator.hpp
@@ -9,24 +9,7 @@ namespace MC::World {
 class Generator {
 public:
     Generator() = default;
-
     Chunk generate(int64_t chunk_x, int64_t chunk_y);
-
-private:
-    Matrix<Chunk::Width, Chunk::Width> ocean_weights_pass(
-            int64_t chunk_x, int64_t chunk_y
-    );
-    Matrix<Chunk::Width, Chunk::Width, Vector<BiomeType::Size>> biome_weights_pass(
-            int64_t chunk_x, int64_t chunk_y,
-            Matrix<Chunk::Width, Chunk::Width> ocean_weight
-    );
-    Matrix<Chunk::Width, Chunk::Width> height_pass(
-            int64_t chunk_x, int64_t chunk_y,
-            Matrix<Chunk::Width, Chunk::Width, Vector<BiomeType::Size>> biome_weights
-    );
-    Matrix<Chunk::Width, Chunk::Width, BiomeType> flat_biome_pass(
-            Matrix<Chunk::Width, Chunk::Width, Vector<BiomeType::Size>> biome_weights
-    );
 };
 
 }
diff --git a/src/World/World.cpp b/src/World/World.cpp
index 7d1b4ee..1782fd4 100644
--- a/src/World/World.cpp
+++ b/src/World/World.cpp
@@ -3,33 +3,55 @@
 namespace MC::World {
 
 std::vector<World::ChunkData> World::get_visible_chunks(Vector<3> position) {
+    auto finished_chunks = load_finished_chunks_from_queue();
     auto visible_chunks = get_visible_chunk_indices(position);
 
-    auto difference = visible_chunks;
+    auto updates = visible_chunks;
     for (auto index : m_visible_chunks) {
-        difference.erase(index);
+        updates.erase(index);
+    }
+    for (auto index : finished_chunks) {
+        updates.insert(index);
     }
 
-    if (!difference.empty()) {
-        for (auto new_index: difference) {
-            auto& data = get_or_generate(new_index);
-            if (!data.mesh.has_value()) {
-                auto mesh = data.chunk->mesh();
-                data.mesh = GFX::Binder::load(mesh);
-            }
-        }
+    if (!updates.empty()) {
+        process_chunk_visibility_updates(updates);
         m_visible_chunks = visible_chunks;
     }
 
     std::vector<World::ChunkData> chunks{};
     chunks.reserve(visible_chunks.size());
     for (auto index : visible_chunks) {
-        chunks.push_back(get_or_generate(index));
+        auto& data = get(index);
+        if (data.status == ChunkStatus::Done) {
+            chunks.push_back(data);
+        }
     }
 
     return chunks;
 }
 
+void World::process_chunk_visibility_updates(std::unordered_set<ChunkIndex>& new_chunks) {
+    for (auto new_index: new_chunks) {
+        auto& data = get(new_index);
+        switch (data.status) {
+            case ChunkStatus::Empty:
+                request_generation(new_index);
+                data.status = ChunkStatus::InFlight;
+                break;
+            case ChunkStatus::InFlight:
+                // Continue waiting...
+                break;
+            case ChunkStatus::Done:
+                if (!data.mesh.has_value()) {
+                    auto mesh = data.chunk.value().mesh();
+                    data.mesh = GFX::Binder::load(mesh);
+                }
+                break;
+        }
+    }
+}
+
 std::unordered_set<ChunkIndex> World::get_visible_chunk_indices(Vector<3> position) const {
     int32_t center_x = std::round(position.x() / Chunk::Width);
     int32_t center_y = std::round(position.z() / Chunk::Width);
@@ -47,11 +69,29 @@ std::unordered_set<ChunkIndex> World::get_visible_chunk_indices(Vector<3> positi
     return indices;
 }
 
-World::ChunkData& World::get_or_generate(ChunkIndex index) {
+std::unordered_set<ChunkIndex> World::load_finished_chunks_from_queue() {
+    std::unordered_set<ChunkIndex> indices;
+    auto results = m_queue.done();
+    for (auto& result : results) {
+        auto& data = get(result.id);
+        data.chunk = {result.res};
+        data.status = ChunkStatus::Done;
+        indices.insert(result.id);
+    }
+
+    return indices;
+}
+
+void World::request_generation(ChunkIndex index) {
+    m_queue.add(index, [=]() {
+        return m_generator.generate(index.x, index.y);
+    });
+}
+
+World::ChunkData& World::get(ChunkIndex index) {
     auto entry = m_chunks.find(index);
     if (entry == m_chunks.end()) {
-        auto chunk = m_generator.generate(index.x, index.y);
-        ChunkData data{index, std::make_shared<Chunk>(chunk)};
+        ChunkData data{index, ChunkStatus::Empty};
 
         m_chunks.insert({index, data});
         return m_chunks.at(index);
@@ -60,5 +100,4 @@ World::ChunkData& World::get_or_generate(ChunkIndex index) {
     return entry->second;
 }
 
-
 }
\ No newline at end of file
diff --git a/src/World/World.hpp b/src/World/World.hpp
index f113bda..dc8f8a7 100644
--- a/src/World/World.hpp
+++ b/src/World/World.hpp
@@ -6,30 +6,39 @@
 #include <utility>
 #include "Generator.hpp"
 #include "ChunkIndex.hpp"
+#include "../Compute/Queue.hpp"
 
 namespace MC::World {
 
 class World {
 public:
-    World() : m_generator(), m_chunks(), m_visible_chunks() {}
+    World() : m_queue(2), m_chunks(), m_visible_chunks() {}
 
-    struct ChunkData {
-        ChunkData(ChunkIndex index, std::shared_ptr<Chunk> chunk)
-            : index(index), chunk(std::move(chunk)), mesh() {}
+    enum class ChunkStatus {
+        Empty,
+        InFlight,
+        Done
+    };
 
+    struct ChunkData {
         ChunkIndex index;
-        std::shared_ptr<Chunk> chunk;
-        std::optional<GFX::BindableMesh> mesh;
+        ChunkStatus status;
+        std::optional<Chunk> chunk = {};
+        std::optional<GFX::BindableMesh> mesh = {};
     };
 
     std::vector<ChunkData> get_visible_chunks(Vector<3> position);
-
 private:
     std::unordered_set<ChunkIndex> get_visible_chunk_indices(Vector<3> position) const;
-    ChunkData& get_or_generate(ChunkIndex index);
+    std::unordered_set<ChunkIndex> load_finished_chunks_from_queue();
+    void process_chunk_visibility_updates(std::unordered_set<ChunkIndex>& new_chunks);
+    void request_generation(ChunkIndex index);
+
+    ChunkData& get(ChunkIndex index);
 
-    uint8_t m_view_distance_radius = 6;
+    uint8_t m_view_distance_radius = 12;
 
+    Compute::Queue<Chunk, ChunkIndex> m_queue;
     Generator m_generator;
 
     std::unordered_map<ChunkIndex, ChunkData> m_chunks;