summary refs log tree commit diff
path: root/src/World
diff options
context:
space:
mode:
Diffstat (limited to 'src/World')
-rw-r--r--src/World/BiomeType.hpp42
-rw-r--r--src/World/BlockType.hpp39
-rw-r--r--src/World/Chunk.cpp59
-rw-r--r--src/World/Chunk.hpp10
-rw-r--r--src/World/Generator.cpp178
-rw-r--r--src/World/Generator.hpp17
-rw-r--r--src/World/World.cpp4
7 files changed, 303 insertions, 46 deletions
diff --git a/src/World/BiomeType.hpp b/src/World/BiomeType.hpp
new file mode 100644
index 0000000..026b3ef
--- /dev/null
+++ b/src/World/BiomeType.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+namespace MC::World {
+
+class BiomeType {
+public:
+    enum Value : uint8_t {
+        Forest,
+        Plains,
+        Desert,
+        Ocean,
+    };
+
+    static constexpr const size_t Size = 4;
+
+    BiomeType() = default;
+    BiomeType(Value biome) : m_biome(biome) {}
+
+    operator Value() const { return m_biome; }
+
+    static std::vector<BiomeType> all() {
+        return {
+            Plains, Forest, Desert, Ocean
+        };
+    }
+
+    static std::vector<BiomeType> all_ground() {
+        return {
+            Plains, Forest, Desert
+        };
+    }
+
+private:
+
+
+    Value m_biome;
+};
+
+}
\ No newline at end of file
diff --git a/src/World/BlockType.hpp b/src/World/BlockType.hpp
index 8d83be0..65afe0d 100644
--- a/src/World/BlockType.hpp
+++ b/src/World/BlockType.hpp
@@ -1,11 +1,40 @@
-#pragma
+#pragma once
+
+#include <cstdint>
+#include <vector>
 
 namespace MC::World {
 
-enum class BlockType : uint8_t {
-    Air,
-    Dirt,
-    Grass,
+class BlockType {
+public:
+    enum Value : uint8_t {
+        Air,
+        Dirt,
+        Grass,
+        Stone,
+        Sand,
+        Water,
+    };
+
+    static constexpr const size_t Size = 6;
+
+    BlockType() = default;
+    BlockType(Value block) : m_block(block) {}
+
+    operator Value() const { return m_block; }
+
+    static std::vector<BlockType> all() {
+        return {
+            Air,
+            Dirt,
+            Grass,
+            Stone,
+            Sand,
+            Water,
+        };
+    }
+private:
+    Value m_block;
 };
 
 }
\ No newline at end of file
diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp
index cd6d4f6..08f08bc 100644
--- a/src/World/Chunk.cpp
+++ b/src/World/Chunk.cpp
@@ -13,9 +13,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 < 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;
                 if (type == BlockType::Air) {
                     continue;
@@ -83,9 +83,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() >= Chunk::Width || neighbor_pos.x() < 0 ||
+        neighbor_pos.y() >= Chunk::Height || neighbor_pos.y() < 0 ||
+        neighbor_pos.z() >= Chunk::Width || neighbor_pos.z() < 0
     ) {
         return true;
     }
@@ -99,30 +99,45 @@ 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;
+
+    float width_step = 1.0f / atlas_width;
+    float height_step = 1.0f / atlas_height;
+
+    auto block_coords = [=](uint8_t x, uint8_t y) {
+        auto t = y * height_step;
+        auto l = x * width_step;
+        auto b = t + height_step;
+        auto r = l + width_step;
+
+        return std::array<Vector<2>, 4>{{
+            {l, b}, {r, b}, {r, t}, {l, t},
+        }};
+    };
+
     switch (type) {
         case BlockType::Dirt:
-            return {{
-                {0.5f, 0.0f}, {1.0f, 0.0f}, {1.0f, 0.5f}, {0.5f, 0.5f},
-            }};
+            return block_coords(1, 0);
         case BlockType::Grass:
             switch (side) {
                 case BlockSide::Front:
                 case BlockSide::Back:
                 case BlockSide::Left:
                 case BlockSide::Right:
-                    return {{
-                        {0.5f, 1.0f}, {0.0f, 1.0f},  {0.0f, 0.5f}, {0.5f, 0.5f},
-                    }};
+                    return block_coords(0, 1);
                 case BlockSide::Top:
-                    return {{
-                        {0.0f, 0.0f}, {0.5f, 0.0f}, {0.5f, 0.5f}, {0.0f, 0.5f},
-                    }};
+                    return block_coords(0, 0);
                 case BlockSide::Bottom:
-                    return {{
-                        {0.5f, 0.0f}, {1.0f, 0.0f}, {1.0f, 0.5f}, {0.5f, 0.5f},
-                    }};
+                    return block_coords(1, 0);
             }
-        default:
+        case BlockType::Stone:
+            return block_coords(1, 1);
+        case BlockType::Sand:
+            return block_coords(0, 2);
+        case BlockType::Water:
+            return block_coords(1, 2);
+        case BlockType::Air:
             return {};
     }
 }
@@ -131,9 +146,9 @@ std::array<Vector<3>, 4> Chunk::face_normals(BlockSide side) {
     auto is_side = [=](BlockSide s) -> float { return s == side; };
 
     Vector<3> normal = {
-            is_side(BlockSide::Right) - is_side(BlockSide::Left),
-            is_side(BlockSide::Top) - is_side(BlockSide::Bottom),
-            is_side(BlockSide::Front) - is_side(BlockSide::Back),
+        is_side(BlockSide::Right) - is_side(BlockSide::Left),
+        is_side(BlockSide::Top) - is_side(BlockSide::Bottom),
+        is_side(BlockSide::Front) - is_side(BlockSide::Back),
     };
 
     return {normal, normal, normal, normal};
diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp
index 7415d7c..cb4f7e1 100644
--- a/src/World/Chunk.hpp
+++ b/src/World/Chunk.hpp
@@ -7,15 +7,15 @@
 #include "BlockSide.hpp"
 #include "../GFX/Binder.hpp"
 
-#define CHUNK_WIDTH 16
-#define CHUNK_HEIGHT 64
-
 namespace MC::World {
 
 class Chunk {
 public:
+    static constexpr const uint32_t Width = 16;
+    static constexpr const uint32_t Height = 64;
+
     Chunk(int64_t x, int64_t y)
-        : m_blocks{}, m_position{(float)x * CHUNK_WIDTH, 0.0f, (float)y * CHUNK_WIDTH} {};
+        : m_blocks{}, 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);
 
@@ -32,7 +32,7 @@ private:
     };
 
     Vector<3> m_position;
-    BlockData m_blocks[CHUNK_WIDTH][CHUNK_HEIGHT][CHUNK_WIDTH];
+    BlockData m_blocks[Chunk::Width][Chunk::Height][Chunk::Width];
 };
 
 }
diff --git a/src/World/Generator.cpp b/src/World/Generator.cpp
index ee7ece9..875a79b 100644
--- a/src/World/Generator.cpp
+++ b/src/World/Generator.cpp
@@ -6,23 +6,46 @@ namespace MC::World {
 Chunk Generator::generate(int64_t chunk_x, int64_t chunk_y) {
     Chunk chunk(chunk_x, chunk_y);
 
-    uint8_t extent = 60;
-    uint8_t base = 4;
+    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);
 
-    for (int x = 0; x < CHUNK_WIDTH; x++) {
-        for (int z = 0; z < CHUNK_WIDTH; z++) {
-            float noise = Math::noise2d(
-                {(float)chunk_x * CHUNK_WIDTH + x, (float)chunk_y * CHUNK_WIDTH + z}
-            ) * extent + base;
+    float extent = 60.0f;
+    float base = 4.0f;
 
-            uint height = std::round(noise);
+    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);
 
-            for (int y = 0; y < CHUNK_HEIGHT; y++) {
+            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 < height) {
-                    type = BlockType::Dirt;
+                if (y < (int32_t)height - 10) {
+                    type = BlockType::Stone;
+                } else if (y < height) {
+                    type = fill_block;
                 } else if (y == height) {
-                    type = BlockType::Grass;
+                    type = top_block;
                 }
 
                 chunk.set(x, y, z, type);
@@ -33,4 +56,135 @@ Chunk Generator::generate(int64_t chunk_x, int64_t chunk_y) {
     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]
+                });
+
+                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};
+                }
+            }
+
+            biomes(x, y) = max_biome.first;
+        }
+    }
+
+    return biomes;
+}
+
 }
\ No newline at end of file
diff --git a/src/World/Generator.hpp b/src/World/Generator.hpp
index 479089f..f184ff9 100644
--- a/src/World/Generator.hpp
+++ b/src/World/Generator.hpp
@@ -2,6 +2,7 @@
 
 #include <cstdint>
 #include "Chunk.hpp"
+#include "BiomeType.hpp"
 
 namespace MC::World {
 
@@ -10,6 +11,22 @@ 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 732e1b9..7d1b4ee 100644
--- a/src/World/World.cpp
+++ b/src/World/World.cpp
@@ -31,8 +31,8 @@ std::vector<World::ChunkData> World::get_visible_chunks(Vector<3> position) {
 }
 
 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);
+    int32_t center_x = std::round(position.x() / Chunk::Width);
+    int32_t center_y = std::round(position.z() / Chunk::Width);
 
     std::unordered_set<ChunkIndex> indices{};
     indices.reserve(m_view_distance_radius * m_view_distance_radius * 4);