summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Compute/Queue.hpp96
-rw-r--r--src/GFX/Binder.cpp2
-rw-r--r--src/GFX/Binder.hpp2
-rw-r--r--src/GFX/Camera.hpp3
-rw-r--r--src/GFX/Image/PPMParser.cpp2
-rw-r--r--src/GFX/Image/RawImage.cpp28
-rw-r--r--src/GFX/Image/RawImage.hpp14
-rw-r--r--src/GFX/Mesh.hpp2
-rw-r--r--src/GFX/Shading/Program.hpp2
-rw-r--r--src/GFX/Shading/Uniform.hpp2
-rw-r--r--src/GFX/Texture.cpp4
-rw-r--r--src/Math/Common.hpp (renamed from src/Math/Math.hpp)0
-rw-r--r--src/Math/Grid.cpp77
-rw-r--r--src/Math/Grid.hpp36
-rw-r--r--src/Math/Interpolation.cpp25
-rw-r--r--src/Math/Interpolation.hpp13
-rw-r--r--src/Math/MVP.cpp2
-rw-r--r--src/Math/MVP.hpp2
-rw-r--r--src/Math/Matrix.hpp2
-rw-r--r--src/Math/Noise.cpp126
-rw-r--r--src/Math/Noise.hpp13
-rw-r--r--src/Math/Perlin.cpp92
-rw-r--r--src/Math/Perlin.hpp38
-rw-r--r--src/Math/Vector.hpp18
-rw-r--r--src/Util/ImageViewer.cpp29
-rw-r--r--src/Util/ImageViewer.hpp7
-rw-r--r--src/Util/Sampler.hpp36
-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
-rw-r--r--src/main.cpp10
35 files changed, 617 insertions, 398 deletions
diff --git a/src/Compute/Queue.hpp b/src/Compute/Queue.hpp
new file mode 100644
index 0000000..6aba1ab
--- /dev/null
+++ b/src/Compute/Queue.hpp
@@ -0,0 +1,96 @@
+#pragma once
+
+#include <functional>
+#include <sys/types.h>
+#include <mutex>
+#include <utility>
+#include <vector>
+#include <iostream>
+#include <thread>
+
+namespace MC::Compute {
+
+template <typename X, typename I = uint>
+class Queue {
+public:
+    explicit Queue(uint workers) : m_control(std::make_shared<Control>()) {
+        for (uint w = 0; w < workers; w++) {
+            std::thread thread{[=]() { Queue::run_thread(m_control); }};
+            thread.detach();
+        }
+    };
+
+    void add(I id, std::function<X()> execute) {
+        std::scoped_lock work_lock(m_control->work.mutex);
+        m_control->work.jobs.push_back({id, std::move(execute)});
+    }
+
+    struct Result {
+        I id{};
+        X res;
+    };
+
+    std::vector<Result> done() {
+        std::vector<Result> done_results;
+
+        std::scoped_lock result_lock(m_control->results.mutex);
+        for (auto r : m_control->results.results) {
+            done_results.push_back(r);
+        }
+        m_control->results.results.clear();
+        return done_results;
+    }
+
+private:
+    struct Job {
+        I id;
+        std::function<X()> execute;
+    };
+
+    struct Work {
+        std::mutex mutex;
+        std::vector<Job> jobs;
+    };
+
+    struct Results {
+        std::mutex mutex;
+        std::vector<Result> results;
+    };
+
+    struct Control {
+        Work work;
+        Results results;
+    };
+
+    [[noreturn]] static void run_thread(std::shared_ptr<Control> control) {
+        using namespace std::chrono_literals;
+
+        while (true) {
+            bool nothing_to_do = true;
+            Job job;
+            {
+                std::scoped_lock work_lock(control->work.mutex);
+                if (!control->work.jobs.empty()) {
+                    job = control->work.jobs.back();
+                    control->work.jobs.pop_back();
+                    nothing_to_do = false;
+                }
+            }
+
+            if (nothing_to_do) {
+                std::this_thread::sleep_for(100ms);
+                continue;
+            }
+
+            auto res = job.execute();
+            {
+                std::scoped_lock result_lock(control->results.mutex);
+                control->results.results.push_back({job.id, res});
+            }
+        }
+    }
+
+    std::shared_ptr<Control> m_control;
+};
+
+}
diff --git a/src/GFX/Binder.cpp b/src/GFX/Binder.cpp
index 0cc844d..0dce06d 100644
--- a/src/GFX/Binder.cpp
+++ b/src/GFX/Binder.cpp
@@ -4,7 +4,7 @@
 
 namespace MC::GFX {
 
-BindableMesh Binder::load(Mesh& mesh) {
+BindableMesh Binder::load(Mesh mesh) {
     auto vao = create_vao();
     if (!mesh.indices().empty()) {
         store_indices(mesh.indices().data(), mesh.indices().size());
diff --git a/src/GFX/Binder.hpp b/src/GFX/Binder.hpp
index 3d3949b..d8a2be9 100644
--- a/src/GFX/Binder.hpp
+++ b/src/GFX/Binder.hpp
@@ -35,7 +35,7 @@ class Binder {
 public:
     Binder() = default;;
 
-    static BindableMesh load(Mesh& mesh);
+    static BindableMesh load(Mesh mesh);
 
 private:
     static uint32_t create_vao();
diff --git a/src/GFX/Camera.hpp b/src/GFX/Camera.hpp
index f03f009..77a210f 100644
--- a/src/GFX/Camera.hpp
+++ b/src/GFX/Camera.hpp
@@ -1,7 +1,6 @@
 #pragma once
 
-#include "../Math/Math.hpp"
-#include "../Math/Rotation.hpp"
+#include "../Math/Common.hpp"
 
 namespace MC::GFX {
 
diff --git a/src/GFX/Image/PPMParser.cpp b/src/GFX/Image/PPMParser.cpp
index 31be0ab..cf1bf77 100644
--- a/src/GFX/Image/PPMParser.cpp
+++ b/src/GFX/Image/PPMParser.cpp
@@ -17,7 +17,7 @@ RawImage PPMParser::parse() {
 
     auto pixel_count = header.width * header.height;
 
-    RawImage image(header.width, header.height, 3);
+    RawImage image(header.width, header.height);
     for (uint64_t pixel_index = 0; pixel_index < pixel_count; pixel_index++) {
         RawImage::Pixel pixel = parse_pixel(header.max_color);
         image.add(pixel);
diff --git a/src/GFX/Image/RawImage.cpp b/src/GFX/Image/RawImage.cpp
index aca8fbc..1222fab 100644
--- a/src/GFX/Image/RawImage.cpp
+++ b/src/GFX/Image/RawImage.cpp
@@ -2,6 +2,19 @@
 
 namespace MC::GFX::Image {
 
+RawImage::RawImage(Util::Sampler<2, float>& sampler, uint32_t width, uint32_t height)
+    : m_pixels(), m_width(width), m_height(height)
+{
+    m_pixels.reserve(width * height);
+    for (int y = 0; y < height; y++) {
+        for (int x = 0; x < width; x++) {
+            auto result = sampler.sample({(float)x, (float)y});
+            auto intensity = (uint8_t)(result * 255);
+            add({intensity, intensity, intensity});
+        }
+    }
+}
+
 void RawImage::add(RawImage::Pixel pixel) {
     m_pixels.push_back(pixel);
 }
@@ -26,4 +39,19 @@ uint8_t RawImage::channels() const {
     return m_channels;
 }
 
+std::string RawImage::string() const {
+    std::stringstream str{};
+
+    bool comma = false;
+    str << "[";
+    for (const auto& pixel : m_pixels) {
+        if (comma) { str << ", "; }
+        str << "{r=" << (uint)pixel.r << ", g=" << (uint)pixel.g << ", b=" << (uint)pixel.r << "}";
+        comma = true;
+    }
+    str << "]";
+
+    return str.str();
+}
+
 }
\ No newline at end of file
diff --git a/src/GFX/Image/RawImage.hpp b/src/GFX/Image/RawImage.hpp
index 2b10cb0..8a837e6 100644
--- a/src/GFX/Image/RawImage.hpp
+++ b/src/GFX/Image/RawImage.hpp
@@ -2,19 +2,23 @@
 
 #include <cstdint>
 #include <cstddef>
+#include <string>
 #include <vector>
+#include "../../Util/Sampler.hpp"
 
 namespace MC::GFX::Image {
 
 class RawImage {
 public:
-    RawImage() : m_pixels(), m_width(0), m_height(0), m_channels(0) {};
+    RawImage() : m_pixels(), m_width(0), m_height(0) {};
 
-    explicit RawImage(uint32_t width, uint32_t height, uint8_t channels)
-        : m_pixels(), m_width(width), m_height(height), m_channels(channels) {
+    RawImage(uint32_t width, uint32_t height)
+        : m_pixels(), m_width(width), m_height(height) {
         m_pixels.reserve(width * height);
     }
 
+    RawImage(Util::Sampler<2, float>& sampler, uint32_t width, uint32_t height);
+
     struct Pixel {
         uint8_t r, g, b;
     };
@@ -27,11 +31,13 @@ public:
     uint32_t width() const;
     uint32_t height() const;
     uint8_t channels() const;
+
+    std::string string() const;
 private:
     std::vector<Pixel> m_pixels;
 
     uint32_t m_width, m_height;
-    uint8_t m_channels;
+    uint8_t m_channels = 3;
 };
 
 }
diff --git a/src/GFX/Mesh.hpp b/src/GFX/Mesh.hpp
index 36a7dac..bfe8eab 100644
--- a/src/GFX/Mesh.hpp
+++ b/src/GFX/Mesh.hpp
@@ -3,7 +3,7 @@
 #include <utility>
 #include <vector>
 #include <cstdint>
-#include "../Math/Math.hpp"
+#include "../Math/Common.hpp"
 
 namespace MC::GFX {
 
diff --git a/src/GFX/Shading/Program.hpp b/src/GFX/Shading/Program.hpp
index b7db953..6b28de0 100644
--- a/src/GFX/Shading/Program.hpp
+++ b/src/GFX/Shading/Program.hpp
@@ -3,7 +3,7 @@
 #include <string>
 #include <vector>
 #include "Shader.hpp"
-#include "../../Math/Math.hpp"
+#include "../../Math/Common.hpp"
 #include "Uniform.hpp"
 
 namespace MC::GFX::Shading {
diff --git a/src/GFX/Shading/Uniform.hpp b/src/GFX/Shading/Uniform.hpp
index 8035dfe..3c14315 100644
--- a/src/GFX/Shading/Uniform.hpp
+++ b/src/GFX/Shading/Uniform.hpp
@@ -3,7 +3,7 @@
 #include <cstdint>
 #include <string>
 #include <utility>
-#include "../../Math/Math.hpp"
+#include "../../Math/Common.hpp"
 
 namespace MC::GFX::Shading {
 
diff --git a/src/GFX/Texture.cpp b/src/GFX/Texture.cpp
index f5474df..ef48631 100644
--- a/src/GFX/Texture.cpp
+++ b/src/GFX/Texture.cpp
@@ -13,6 +13,10 @@ Texture::Texture(const Image::RawImage& image) {
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
+    // If this is not here, odd-numbered rows (like those in i.e. 5x5 textures)
+    // will begin at the incorrect byte, causing color artifacts.
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width(), image.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image.raw());
     glGenerateMipmap(GL_TEXTURE_2D);
 
diff --git a/src/Math/Math.hpp b/src/Math/Common.hpp
index 94840b1..94840b1 100644
--- a/src/Math/Math.hpp
+++ b/src/Math/Common.hpp
diff --git a/src/Math/Grid.cpp b/src/Math/Grid.cpp
new file mode 100644
index 0000000..422cf87
--- /dev/null
+++ b/src/Math/Grid.cpp
@@ -0,0 +1,77 @@
+#include "Grid.hpp"
+
+namespace Math {
+
+GridCellBoundaries grid_cell_for_point(Vector<2> point) {
+    auto x1 = std::trunc(point.x());
+    auto y1 = std::trunc(point.y());
+    auto x2 = x1 + 1.0f;
+    auto y2 = y1 + 1.0f;
+
+    return GridCellBoundaries{x1, x2, y1, y2};
+}
+
+Vector<2> GridCellBoundaries::top_left() const {
+    return {x1, y1};
+}
+
+Vector<2> GridCellBoundaries::top_right() const {
+    return {x2, y1};
+}
+
+Vector<2> GridCellBoundaries::bottom_left() const {
+    return {x1, y2};
+}
+
+Vector<2> GridCellBoundaries::bottom_right() const {
+    return {x2, y2};
+}
+
+CubeCellBoundaries cube_cell_for_point(Vector<3> point) {
+    auto x1 = std::trunc(point.x());
+    auto y1 = std::trunc(point.y());
+    auto z1 = std::trunc(point.z());
+    auto x2 = x1 + 1.0f;
+    auto y2 = y1 + 1.0f;
+    auto z2 = z1 + 1.0f;
+
+    return CubeCellBoundaries{x1, x2, y1, y2, z1, z2};
+}
+
+Vector<3> CubeCellBoundaries::front_top_left() const {
+    return {x1, y1, z1};
+}
+
+Vector<3> CubeCellBoundaries::front_top_right() const {
+    return {x2, y1, z1};
+}
+
+Vector<3> CubeCellBoundaries::front_bottom_left() const {
+    return {x1, y2, z1};
+}
+
+Vector<3> CubeCellBoundaries::front_bottom_right() const {
+    return {x2, y2, z1};
+}
+
+Vector<3> CubeCellBoundaries::back_top_left() const {
+    return {x1, y1, z2};
+}
+
+Vector<3> CubeCellBoundaries::back_top_right() const {
+    return {x2, y1, z2};
+}
+
+Vector<3> CubeCellBoundaries::back_bottom_left() const {
+    return {x1, y2, z2};
+}
+
+Vector<3> CubeCellBoundaries::back_bottom_right() const {
+    return {x2, y2, z2};
+}
+
+GridCellBoundaries CubeCellBoundaries::grid_cell() const {
+    return {x1, x2, y1, y2};
+}
+
+}
\ No newline at end of file
diff --git a/src/Math/Grid.hpp b/src/Math/Grid.hpp
new file mode 100644
index 0000000..1c1a7ca
--- /dev/null
+++ b/src/Math/Grid.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "Common.hpp"
+#include "Matrix.hpp"
+
+namespace Math {
+
+struct GridCellBoundaries {
+    float x1, x2, y1, y2;
+
+    [[nodiscard]] Vector<2> top_left() const;
+    [[nodiscard]] Vector<2> top_right() const;
+    [[nodiscard]] Vector<2> bottom_left() const;
+    [[nodiscard]] Vector<2> bottom_right() const;
+};
+
+GridCellBoundaries grid_cell_for_point(Vector<2> point);
+
+struct CubeCellBoundaries {
+    float x1, x2, y1, y2, z1, z2;
+
+    [[nodiscard]] Vector<3> front_top_left() const;
+    [[nodiscard]] Vector<3> front_top_right() const;
+    [[nodiscard]] Vector<3> front_bottom_left() const;
+    [[nodiscard]] Vector<3> front_bottom_right() const;
+    [[nodiscard]] Vector<3> back_top_left() const;
+    [[nodiscard]] Vector<3> back_top_right() const;
+    [[nodiscard]] Vector<3> back_bottom_left() const;
+    [[nodiscard]] Vector<3> back_bottom_right() const;
+
+    [[nodiscard]] GridCellBoundaries grid_cell() const;
+};
+
+CubeCellBoundaries cube_cell_for_point(Vector<3> point);
+
+}
\ No newline at end of file
diff --git a/src/Math/Interpolation.cpp b/src/Math/Interpolation.cpp
new file mode 100644
index 0000000..a1e6b69
--- /dev/null
+++ b/src/Math/Interpolation.cpp
@@ -0,0 +1,25 @@
+#include "Interpolation.hpp"
+#include <functional>
+#include "Common.hpp"
+
+namespace Math {
+
+float linear_interpolation(Vector<2> val, float left, float right, float pos) {
+    return val.x() + (pos - left) * (val.y() - val.x()) / (right - left);
+}
+
+float bilinear_interpolation(Matrix<2, 2> val, GridCellBoundaries cell, Vector<2> pos) {
+    auto r1 = linear_interpolation(val.row(0), cell.x1, cell.x2, pos.x());
+    auto r2 = linear_interpolation(val.row(1), cell.x1, cell.x2, pos.x());
+
+    return linear_interpolation({r1, r2}, cell.y1, cell.y2, pos.y());
+}
+
+float trilinear_interpolation(Matrix<2, 2> val_front, Matrix<2, 2> val_back, CubeCellBoundaries cell, Vector<3> pos) {
+    auto r1 = bilinear_interpolation(val_front, cell.grid_cell(), {pos.x(), pos.y()});
+    auto r2 = bilinear_interpolation(val_back, cell.grid_cell(), {pos.x(), pos.y()});
+
+    return linear_interpolation({r1, r2}, cell.z1, cell.z2, pos.z());
+}
+
+}
\ No newline at end of file
diff --git a/src/Math/Interpolation.hpp b/src/Math/Interpolation.hpp
new file mode 100644
index 0000000..9eaf604
--- /dev/null
+++ b/src/Math/Interpolation.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <functional>
+#include "Common.hpp"
+#include "Grid.hpp"
+
+namespace Math {
+
+float linear_interpolation(Vector<2> val, float left, float right, float pos);
+float bilinear_interpolation(Matrix<2, 2> val, GridCellBoundaries cell, Vector<2> pos);
+float trilinear_interpolation(Matrix<2, 2> val_front, Matrix<2, 2> val_back, CubeCellBoundaries cell, Vector<3> pos);
+
+}
\ No newline at end of file
diff --git a/src/Math/MVP.cpp b/src/Math/MVP.cpp
index 754c656..dd5b5e7 100644
--- a/src/Math/MVP.cpp
+++ b/src/Math/MVP.cpp
@@ -1,6 +1,6 @@
 #include <cmath>
 #include "MVP.hpp"
-#include "Math.hpp"
+#include "Common.hpp"
 
 namespace Math::MVP {
 
diff --git a/src/Math/MVP.hpp b/src/Math/MVP.hpp
index 0abad66..af4fbbc 100644
--- a/src/Math/MVP.hpp
+++ b/src/Math/MVP.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include "Math.hpp"
+#include "Common.hpp"
 
 namespace Math::MVP {
 
diff --git a/src/Math/Matrix.hpp b/src/Math/Matrix.hpp
index 545b6a5..276dc4a 100644
--- a/src/Math/Matrix.hpp
+++ b/src/Math/Matrix.hpp
@@ -15,7 +15,7 @@ public:
         std::fill(elements, elements + R * C, scalar);
     };
 
-    template<typename ...Args>
+    template<typename ...Args, typename std::enable_if<sizeof...(Args) == R * C, int>::type = 0>
     Matrix<R, C, T>(Args... args): elements{ args... } {};
 
     Matrix<R, C, T>(T values[R * C]) {
diff --git a/src/Math/Noise.cpp b/src/Math/Noise.cpp
deleted file mode 100644
index 1ef8ce5..0000000
--- a/src/Math/Noise.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-#include <vector>
-#include "Matrix.hpp"
-#include "Noise.hpp"
-
-namespace Math {
-
-float noise2d(Vector<2> pos) {
-    struct Layer {
-        float scale;
-        Vector<2> offset;
-        float weight;
-    };
-
-    const std::vector<Layer> layers = {
-        {0.1f, {0.0f, 0.0f}, 0.2f},
-        {0.05f, {10.0f, 50.0f}, 0.5f},
-        {0.01f, {250.0f, 20.0f}, 0.5f},
-    };
-
-    auto wrap = [](auto x) -> float {
-        float wx = std::fmod(x, NOISE_SEED_SIZE);
-        return wx < 0 ? wx + NOISE_SEED_SIZE : wx;
-    };
-
-    auto total_weight = 0.0f;
-    auto res = 0.0f;
-    for (const auto& layer : layers) {
-        auto pos_with_scale_and_offset = (pos - layer.offset) * layer.scale;
-        auto p = pos_with_scale_and_offset.map(wrap);
-
-        auto x = p.x();
-        auto y = p.y();
-        auto x1 = x - std::fmod(x, 1.0f);
-        auto y2 = y - std::fmod(y, 1.0f);
-        auto x2 = x1 + 1.0f;
-        auto y1 = y2 + 1.0f;
-
-        auto value = [=](float x, float y){
-            return noise_seed[(uint)wrap(x)][(uint)wrap(y)] / 255.0f;
-        };
-
-        auto q12 = value(x1, y2);
-        auto q22 = value(x2, y2);
-        auto q11 = value(x1, y1);
-        auto q21 = value(x2, y1);
-
-        auto r1 = q11 * (x2 - x) / (x2 - x1) + q21 * (x - x1) / (x2 - x1);
-        auto r2 = q12 * (x2 - x) / (x2 - x1) + q22 * (x - x1) / (x2 - x1);
-
-        auto r = r1 * (y2 - y) / (y2 - y1) + r2 * (y - y1) / (y2 - y1);
-
-        total_weight += layer.weight;
-        res += r * layer.weight;
-    }
-
-    return res / total_weight;
-}
-
-const uint8_t noise_seed[NOISE_SEED_SIZE][NOISE_SEED_SIZE] = {
-    207, 144, 250, 28, 124, 140, 99, 120, 20, 114, 224, 134, 187, 238, 129, 197, 94, 131, 49, 63, 33, 228, 184, 237, 192, 55, 189, 79, 142, 200, 176, 26, 18, 28, 140, 95, 35, 102, 26, 17, 118, 157, 20, 195, 52, 234, 94, 23, 55, 186, 27, 32, 53, 178, 229, 200, 181, 107, 44, 182, 104, 83, 204, 115,
-    116, 63, 190, 252, 77, 56, 203, 31, 111, 137, 189, 159, 58, 252, 129, 249, 96, 228, 88, 17, 50, 236, 110, 37, 180, 100, 105, 235, 91, 245, 206, 123, 6, 248, 231, 1, 193, 151, 255, 177, 7, 27, 240, 231, 49, 54, 191, 93, 99, 219, 8, 177, 12, 240, 143, 155, 229, 170, 160, 16, 102, 160, 66, 194,
-    137, 130, 104, 225, 17, 37, 207, 183, 99, 161, 219, 134, 201, 6, 165, 140, 159, 11, 134, 55, 122, 232, 241, 41, 105, 183, 69, 127, 100, 69, 226, 180, 142, 209, 17, 75, 120, 224, 152, 119, 84, 18, 247, 198, 153, 107, 115, 142, 79, 220, 251, 161, 227, 202, 183, 34, 21, 151, 240, 80, 75, 149, 59, 216,
-    222, 200, 50, 244, 62, 67, 7, 11, 98, 165, 112, 24, 217, 15, 61, 141, 228, 177, 172, 65, 241, 5, 69, 129, 102, 108, 81, 153, 60, 206, 111, 14, 198, 101, 186, 44, 133, 245, 154, 238, 212, 218, 240, 66, 235, 54, 148, 234, 212, 243, 124, 59, 77, 143, 232, 95, 100, 88, 33, 175, 73, 15, 76, 158,
-    157, 28, 212, 83, 1, 180, 42, 78, 76, 67, 130, 176, 248, 31, 204, 4, 152, 158, 252, 211, 14, 9, 16, 230, 9, 180, 59, 13, 140, 158, 222, 213, 147, 238, 247, 189, 230, 173, 86, 231, 174, 154, 190, 253, 162, 241, 89, 122, 33, 187, 242, 206, 156, 53, 171, 31, 113, 25, 183, 79, 53, 15, 74, 187,
-    98, 226, 172, 127, 160, 169, 246, 2, 117, 180, 28, 42, 252, 84, 36, 59, 171, 249, 206, 5, 206, 62, 64, 109, 96, 183, 177, 130, 245, 243, 78, 253, 194, 94, 154, 243, 239, 171, 162, 100, 117, 254, 70, 170, 203, 46, 180, 136, 144, 123, 28, 204, 66, 145, 90, 202, 127, 99, 216, 17, 39, 98, 175, 129,
-    4, 4, 22, 92, 12, 0, 12, 15, 133, 218, 110, 216, 83, 234, 60, 194, 6, 83, 6, 237, 46, 181, 164, 121, 138, 60, 109, 103, 51, 215, 99, 182, 207, 168, 199, 220, 221, 95, 168, 69, 45, 36, 187, 216, 155, 145, 136, 151, 2, 139, 181, 12, 216, 49, 239, 1, 148, 106, 253, 92, 106, 76, 102, 140,
-    76, 137, 9, 9, 133, 64, 184, 242, 0, 91, 150, 128, 236, 129, 73, 231, 166, 146, 32, 152, 0, 14, 117, 138, 15, 27, 195, 14, 140, 111, 61, 124, 102, 35, 252, 145, 173, 121, 113, 77, 140, 150, 75, 93, 123, 112, 117, 12, 55, 18, 235, 28, 29, 198, 224, 129, 195, 172, 48, 12, 22, 131, 120, 94,
-    154, 93, 49, 28, 49, 226, 43, 231, 226, 165, 37, 21, 245, 32, 16, 153, 193, 43, 20, 154, 87, 66, 148, 81, 52, 129, 228, 156, 103, 254, 2, 85, 245, 158, 96, 177, 112, 57, 231, 84, 162, 154, 207, 213, 106, 58, 17, 176, 67, 112, 13, 64, 36, 160, 183, 217, 2, 33, 228, 133, 89, 228, 45, 154,
-    47, 216, 56, 62, 190, 211, 1, 103, 229, 58, 15, 120, 252, 190, 197, 37, 53, 81, 77, 216, 70, 114, 117, 151, 170, 163, 29, 20, 55, 14, 9, 210, 12, 81, 40, 29, 176, 75, 207, 188, 1, 204, 119, 183, 205, 190, 46, 200, 50, 192, 248, 36, 126, 134, 38, 91, 255, 110, 243, 77, 82, 106, 32, 16,
-    194, 253, 226, 238, 141, 232, 99, 79, 103, 221, 142, 58, 180, 139, 173, 179, 51, 171, 208, 149, 248, 8, 26, 201, 90, 232, 209, 148, 179, 17, 234, 110, 245, 48, 94, 46, 148, 225, 108, 225, 94, 76, 200, 62, 209, 235, 106, 69, 145, 25, 238, 179, 18, 21, 165, 152, 157, 106, 229, 29, 221, 126, 130, 210,
-    152, 130, 141, 166, 25, 104, 74, 80, 242, 225, 228, 118, 12, 156, 42, 186, 218, 20, 226, 167, 182, 28, 69, 87, 236, 226, 73, 78, 218, 51, 177, 213, 69, 224, 131, 69, 188, 140, 97, 66, 58, 71, 156, 206, 14, 32, 79, 206, 32, 80, 23, 83, 111, 29, 146, 244, 118, 12, 27, 32, 140, 76, 191, 177,
-    116, 3, 79, 2, 206, 245, 107, 37, 129, 243, 8, 32, 231, 166, 225, 159, 77, 212, 199, 128, 155, 113, 222, 240, 73, 227, 36, 15, 221, 57, 144, 196, 84, 124, 173, 69, 100, 106, 178, 167, 126, 149, 136, 33, 227, 168, 178, 112, 118, 20, 120, 56, 52, 38, 134, 225, 168, 68, 223, 80, 124, 8, 113, 238,
-    11, 227, 96, 10, 129, 0, 193, 198, 154, 0, 96, 224, 92, 12, 157, 175, 76, 39, 2, 120, 140, 190, 104, 34, 1, 136, 72, 234, 15, 73, 132, 60, 190, 52, 18, 88, 208, 2, 61, 194, 203, 198, 218, 45, 3, 186, 44, 181, 149, 90, 179, 230, 241, 237, 75, 116, 198, 125, 48, 118, 171, 88, 51, 221,
-    202, 48, 120, 134, 123, 99, 106, 237, 91, 164, 199, 46, 63, 219, 116, 74, 63, 53, 161, 182, 85, 247, 196, 181, 44, 225, 199, 226, 4, 27, 59, 125, 168, 233, 187, 184, 95, 98, 248, 199, 154, 10, 178, 174, 164, 213, 116, 129, 39, 182, 248, 172, 48, 23, 22, 162, 242, 160, 196, 74, 36, 51, 58, 101,
-    19, 129, 191, 233, 81, 179, 157, 58, 114, 86, 3, 104, 192, 15, 14, 251, 13, 111, 40, 84, 208, 245, 99, 72, 24, 172, 163, 187, 92, 28, 215, 88, 204, 249, 128, 66, 252, 191, 227, 86, 107, 154, 52, 175, 14, 107, 217, 254, 73, 109, 124, 142, 127, 211, 99, 249, 145, 124, 78, 199, 241, 163, 76, 119,
-    27, 130, 244, 202, 161, 215, 81, 160, 245, 182, 27, 92, 194, 190, 96, 31, 136, 170, 93, 173, 22, 54, 174, 6, 229, 233, 131, 78, 228, 97, 70, 27, 74, 238, 238, 91, 206, 2, 158, 19, 154, 37, 16, 245, 13, 87, 219, 105, 193, 24, 97, 113, 131, 170, 209, 191, 114, 115, 11, 208, 227, 86, 137, 13,
-    74, 185, 127, 50, 104, 186, 226, 142, 254, 141, 181, 179, 153, 168, 216, 187, 128, 245, 54, 230, 89, 11, 74, 176, 77, 181, 83, 100, 107, 191, 40, 76, 94, 77, 226, 246, 64, 233, 21, 148, 70, 124, 60, 34, 212, 27, 133, 165, 199, 128, 87, 100, 125, 177, 249, 49, 190, 189, 117, 93, 232, 155, 236, 209,
-    196, 31, 47, 52, 100, 106, 129, 227, 125, 152, 224, 152, 149, 126, 230, 178, 251, 132, 232, 28, 218, 185, 179, 33, 230, 41, 18, 64, 241, 151, 94, 61, 36, 1, 134, 46, 188, 248, 108, 88, 215, 127, 43, 112, 168, 171, 235, 71, 70, 73, 153, 229, 31, 79, 208, 19, 51, 22, 166, 178, 230, 34, 218, 100,
-    130, 241, 104, 185, 33, 236, 47, 77, 87, 57, 200, 230, 223, 156, 242, 121, 14, 153, 69, 206, 173, 187, 26, 28, 71, 89, 63, 198, 139, 36, 184, 140, 221, 43, 76, 249, 191, 165, 231, 118, 183, 120, 138, 20, 2, 243, 36, 135, 83, 37, 12, 72, 211, 203, 7, 144, 205, 131, 222, 57, 254, 164, 123, 38,
-    74, 157, 69, 64, 232, 55, 75, 52, 78, 43, 69, 152, 72, 144, 238, 252, 83, 202, 88, 208, 248, 35, 221, 2, 5, 215, 83, 31, 81, 227, 46, 63, 234, 146, 208, 137, 119, 33, 171, 223, 52, 233, 30, 115, 153, 74, 141, 183, 122, 220, 5, 218, 137, 98, 1, 129, 230, 10, 204, 146, 134, 170, 237, 94,
-    92, 215, 53, 102, 161, 122, 189, 216, 141, 91, 181, 103, 23, 200, 54, 201, 144, 107, 2, 233, 42, 152, 5, 111, 67, 134, 192, 235, 73, 129, 169, 218, 44, 200, 245, 225, 100, 188, 230, 60, 89, 41, 44, 194, 218, 174, 252, 196, 160, 119, 22, 66, 189, 227, 171, 88, 143, 232, 30, 124, 66, 38, 117, 52,
-    149, 5, 169, 140, 176, 50, 94, 236, 80, 69, 77, 100, 39, 179, 105, 130, 174, 18, 127, 254, 62, 49, 147, 52, 237, 212, 231, 7, 99, 224, 77, 194, 237, 88, 144, 170, 25, 202, 58, 144, 13, 145, 70, 202, 97, 128, 71, 243, 167, 31, 3, 227, 103, 175, 25, 52, 123, 217, 105, 224, 155, 4, 208, 216,
-    4, 232, 106, 162, 248, 219, 176, 246, 78, 155, 26, 110, 4, 144, 246, 218, 169, 42, 155, 61, 176, 69, 225, 197, 150, 0, 225, 231, 252, 78, 83, 97, 243, 149, 218, 242, 102, 63, 3, 239, 206, 140, 228, 153, 19, 202, 41, 42, 251, 10, 234, 175, 93, 87, 38, 180, 69, 250, 171, 187, 169, 0, 176, 152,
-    41, 15, 92, 220, 220, 184, 158, 141, 237, 48, 0, 38, 248, 223, 232, 3, 156, 170, 23, 150, 152, 187, 187, 169, 157, 40, 3, 239, 36, 217, 252, 213, 150, 126, 31, 22, 116, 100, 62, 37, 73, 12, 255, 166, 254, 82, 21, 95, 63, 240, 156, 61, 62, 203, 40, 19, 98, 125, 250, 34, 78, 2, 147, 235,
-    169, 53, 190, 127, 0, 183, 155, 184, 76, 60, 62, 106, 28, 96, 141, 54, 215, 110, 91, 99, 108, 158, 81, 73, 215, 227, 192, 140, 134, 229, 152, 223, 33, 148, 208, 237, 153, 46, 252, 157, 182, 36, 135, 4, 218, 212, 230, 56, 109, 84, 193, 151, 38, 47, 53, 73, 13, 154, 243, 119, 177, 38, 71, 149,
-    53, 230, 241, 131, 234, 173, 13, 65, 29, 86, 83, 117, 71, 170, 144, 241, 193, 61, 181, 175, 99, 156, 242, 182, 69, 215, 104, 30, 56, 234, 178, 132, 170, 188, 137, 67, 195, 64, 236, 79, 204, 239, 91, 140, 104, 158, 151, 98, 211, 149, 64, 117, 254, 166, 175, 0, 99, 50, 59, 60, 98, 172, 223, 131,
-    161, 125, 153, 73, 31, 35, 22, 36, 61, 135, 114, 119, 104, 210, 62, 232, 42, 147, 117, 52, 83, 17, 40, 199, 201, 184, 160, 176, 179, 123, 73, 235, 114, 147, 216, 23, 19, 191, 192, 169, 232, 206, 202, 191, 63, 130, 104, 139, 6, 181, 251, 89, 239, 32, 100, 201, 110, 201, 175, 187, 245, 101, 81, 18,
-    171, 97, 221, 252, 189, 194, 107, 230, 36, 1, 17, 75, 170, 98, 196, 90, 87, 127, 204, 20, 234, 91, 178, 47, 125, 167, 167, 93, 157, 103, 76, 67, 12, 251, 179, 128, 62, 73, 170, 23, 14, 173, 115, 50, 81, 145, 176, 98, 29, 225, 30, 166, 70, 216, 5, 8, 238, 143, 179, 125, 39, 107, 246, 40,
-    194, 207, 103, 247, 78, 10, 182, 203, 182, 80, 41, 206, 214, 48, 132, 202, 144, 198, 157, 221, 200, 216, 191, 214, 89, 249, 201, 41, 165, 178, 251, 227, 64, 222, 158, 115, 101, 210, 239, 170, 113, 102, 217, 195, 25, 25, 103, 141, 250, 12, 93, 197, 79, 134, 233, 71, 45, 73, 107, 15, 180, 57, 134, 138,
-    95, 214, 206, 194, 64, 111, 31, 160, 177, 147, 245, 103, 150, 245, 32, 238, 167, 187, 176, 175, 128, 112, 29, 244, 125, 96, 236, 128, 252, 170, 141, 65, 139, 146, 74, 157, 194, 223, 161, 217, 179, 200, 60, 248, 146, 122, 79, 146, 7, 218, 21, 238, 54, 79, 158, 243, 6, 34, 61, 220, 189, 227, 178, 192,
-    222, 188, 15, 192, 2, 76, 12, 162, 229, 75, 25, 53, 165, 61, 22, 166, 62, 161, 168, 84, 249, 68, 84, 13, 249, 47, 237, 229, 135, 215, 173, 110, 136, 54, 177, 38, 218, 18, 48, 141, 60, 159, 136, 209, 27, 132, 212, 88, 128, 30, 106, 236, 44, 214, 182, 35, 153, 112, 168, 170, 235, 49, 95, 132,
-    66, 32, 233, 220, 253, 48, 177, 183, 227, 113, 216, 133, 83, 64, 66, 172, 82, 184, 106, 244, 20, 182, 239, 4, 153, 62, 99, 131, 116, 181, 190, 117, 201, 192, 136, 87, 19, 69, 194, 56, 53, 208, 239, 24, 104, 230, 215, 220, 81, 74, 215, 250, 91, 228, 37, 216, 231, 7, 95, 10, 102, 200, 148, 198,
-    205, 77, 227, 103, 66, 192, 76, 154, 91, 69, 190, 40, 32, 148, 59, 226, 35, 191, 215, 133, 229, 58, 241, 90, 26, 145, 113, 133, 220, 185, 171, 125, 198, 56, 103, 18, 51, 121, 198, 224, 71, 137, 26, 35, 165, 46, 0, 45, 3, 83, 162, 49, 98, 252, 76, 80, 45, 148, 189, 181, 239, 101, 53, 166,
-    78, 220, 177, 136, 4, 138, 219, 149, 239, 56, 75, 100, 68, 155, 14, 48, 56, 103, 126, 1, 233, 131, 66, 233, 242, 86, 253, 149, 185, 162, 47, 212, 52, 36, 222, 239, 140, 27, 232, 239, 209, 194, 209, 228, 41, 248, 65, 102, 7, 240, 208, 166, 39, 29, 149, 62, 67, 160, 0, 133, 216, 137, 215, 166,
-    108, 12, 86, 237, 63, 205, 150, 238, 27, 207, 236, 21, 176, 172, 73, 64, 112, 34, 38, 63, 20, 157, 254, 88, 226, 73, 109, 87, 133, 18, 157, 213, 211, 42, 108, 233, 54, 187, 254, 133, 194, 5, 167, 136, 253, 38, 183, 24, 58, 195, 82, 198, 172, 254, 155, 30, 214, 40, 218, 255, 170, 124, 185, 250,
-    27, 33, 74, 140, 166, 249, 234, 208, 81, 190, 69, 122, 90, 130, 135, 81, 181, 85, 220, 177, 15, 81, 179, 155, 90, 195, 164, 184, 157, 94, 143, 243, 67, 171, 76, 153, 13, 0, 88, 4, 146, 45, 102, 211, 54, 120, 30, 171, 160, 168, 161, 236, 141, 210, 221, 191, 11, 145, 16, 227, 214, 221, 69, 90,
-    46, 215, 11, 166, 83, 157, 127, 34, 140, 101, 230, 125, 103, 109, 244, 250, 131, 181, 206, 216, 152, 99, 240, 6, 185, 252, 237, 68, 221, 222, 81, 2, 84, 238, 230, 47, 162, 73, 111, 123, 185, 110, 83, 76, 16, 112, 241, 10, 58, 228, 234, 239, 143, 32, 53, 168, 212, 53, 55, 191, 112, 222, 40, 88,
-    68, 192, 85, 84, 97, 14, 68, 166, 203, 226, 142, 156, 27, 181, 101, 116, 68, 156, 133, 216, 82, 220, 51, 19, 169, 104, 173, 191, 94, 27, 146, 20, 192, 194, 35, 33, 37, 219, 180, 3, 130, 186, 23, 25, 89, 240, 97, 212, 7, 39, 84, 122, 242, 242, 100, 252, 122, 87, 250, 187, 225, 127, 173, 173,
-    227, 225, 163, 19, 176, 205, 243, 252, 154, 185, 195, 253, 12, 111, 213, 109, 14, 130, 17, 3, 25, 226, 234, 240, 82, 250, 244, 57, 205, 55, 224, 114, 138, 18, 253, 219, 0, 152, 125, 19, 97, 212, 219, 234, 199, 74, 163, 31, 110, 210, 55, 214, 199, 64, 252, 41, 187, 149, 155, 225, 193, 201, 96, 13,
-    154, 36, 139, 225, 17, 5, 61, 145, 108, 77, 64, 221, 205, 41, 212, 147, 171, 214, 84, 251, 128, 150, 175, 221, 115, 36, 177, 97, 83, 73, 145, 95, 163, 9, 247, 64, 126, 88, 78, 202, 52, 69, 229, 89, 56, 68, 64, 51, 156, 134, 120, 229, 199, 77, 37, 167, 144, 105, 70, 16, 152, 103, 163, 210,
-    22, 38, 4, 205, 218, 252, 207, 109, 231, 79, 146, 65, 35, 29, 138, 142, 199, 53, 145, 97, 214, 245, 196, 190, 223, 105, 180, 111, 124, 57, 135, 229, 177, 180, 166, 197, 194, 124, 105, 215, 237, 214, 36, 175, 200, 76, 170, 5, 123, 205, 210, 148, 81, 167, 212, 234, 36, 203, 48, 247, 215, 18, 45, 229,
-    245, 131, 6, 89, 88, 50, 203, 106, 136, 58, 151, 40, 60, 50, 24, 164, 246, 27, 220, 53, 95, 21, 129, 248, 84, 187, 202, 240, 118, 56, 20, 32, 228, 22, 36, 90, 61, 244, 147, 198, 163, 224, 14, 222, 132, 11, 114, 76, 85, 156, 163, 18, 30, 25, 250, 98, 116, 97, 243, 197, 211, 168, 97, 184,
-    114, 130, 95, 14, 53, 2, 148, 248, 21, 168, 85, 19, 173, 212, 38, 180, 133, 13, 245, 181, 116, 120, 162, 0, 183, 9, 166, 215, 62, 196, 71, 24, 175, 55, 135, 112, 97, 240, 17, 56, 167, 215, 150, 247, 255, 120, 214, 59, 177, 238, 107, 85, 53, 185, 69, 90, 179, 30, 38, 130, 7, 58, 59, 193,
-    211, 99, 190, 32, 199, 97, 54, 10, 17, 119, 50, 76, 249, 93, 151, 129, 247, 66, 125, 158, 69, 189, 56, 157, 226, 169, 113, 169, 91, 168, 199, 230, 175, 93, 178, 177, 128, 247, 70, 212, 106, 17, 243, 45, 165, 153, 51, 209, 5, 106, 145, 243, 135, 194, 22, 236, 198, 247, 164, 124, 174, 86, 241, 2,
-    56, 236, 66, 227, 117, 89, 171, 71, 180, 197, 169, 160, 94, 183, 115, 91, 41, 125, 197, 19, 53, 30, 54, 132, 5, 185, 195, 215, 207, 8, 187, 178, 44, 175, 175, 247, 134, 184, 234, 40, 164, 24, 129, 46, 228, 136, 82, 128, 20, 153, 90, 225, 118, 224, 36, 18, 175, 237, 13, 141, 42, 1, 234, 106,
-    15, 222, 218, 115, 89, 47, 111, 91, 84, 91, 113, 161, 15, 21, 122, 110, 109, 28, 24, 33, 216, 248, 92, 19, 246, 252, 154, 29, 179, 37, 144, 247, 253, 181, 101, 254, 236, 116, 103, 19, 31, 71, 94, 48, 112, 115, 147, 148, 186, 158, 102, 107, 43, 200, 23, 172, 125, 96, 200, 71, 123, 211, 224, 121,
-    120, 122, 131, 128, 3, 64, 205, 238, 142, 122, 48, 41, 38, 29, 98, 163, 160, 157, 108, 51, 35, 170, 21, 101, 166, 50, 226, 174, 243, 216, 33, 239, 166, 230, 108, 121, 173, 254, 121, 37, 108, 155, 126, 152, 3, 185, 3, 31, 101, 231, 144, 243, 53, 8, 49, 94, 32, 53, 88, 23, 219, 56, 203, 3,
-    253, 19, 215, 46, 168, 71, 17, 112, 224, 152, 45, 29, 124, 120, 173, 218, 108, 60, 214, 146, 103, 194, 29, 174, 51, 231, 161, 62, 89, 198, 180, 14, 93, 81, 14, 125, 219, 254, 152, 155, 149, 133, 210, 223, 244, 84, 246, 3, 145, 64, 104, 156, 21, 237, 19, 93, 41, 33, 49, 247, 246, 49, 229, 167,
-    93, 177, 189, 130, 110, 231, 173, 219, 0, 111, 168, 170, 25, 113, 96, 41, 100, 189, 115, 20, 97, 203, 247, 188, 175, 170, 171, 193, 89, 137, 151, 0, 130, 22, 29, 95, 60, 10, 200, 83, 73, 187, 51, 67, 121, 242, 117, 91, 30, 212, 3, 226, 32, 157, 64, 249, 94, 127, 214, 12, 234, 97, 28, 201,
-    93, 40, 48, 133, 19, 13, 9, 224, 198, 250, 208, 33, 82, 75, 222, 212, 94, 206, 151, 186, 208, 80, 156, 247, 171, 52, 78, 92, 166, 4, 200, 225, 59, 181, 160, 126, 77, 63, 215, 72, 141, 83, 239, 81, 229, 99, 240, 65, 132, 168, 9, 8, 216, 207, 254, 61, 160, 252, 140, 92, 121, 4, 223, 186,
-    30, 123, 70, 200, 118, 9, 124, 250, 42, 172, 80, 213, 135, 254, 92, 66, 110, 71, 18, 51, 147, 78, 229, 129, 175, 209, 68, 148, 98, 148, 46, 38, 99, 29, 199, 66, 90, 243, 104, 172, 102, 161, 209, 198, 217, 134, 110, 135, 79, 254, 192, 143, 21, 249, 52, 183, 25, 192, 243, 190, 67, 226, 100, 134,
-    44, 106, 27, 60, 199, 16, 206, 63, 9, 176, 10, 114, 113, 238, 64, 57, 12, 209, 31, 88, 200, 170, 148, 11, 227, 93, 160, 57, 73, 104, 13, 159, 69, 152, 50, 123, 153, 37, 195, 67, 4, 114, 158, 44, 111, 227, 250, 64, 212, 43, 3, 36, 103, 169, 179, 0, 22, 46, 125, 3, 0, 173, 37, 201,
-    43, 223, 240, 62, 89, 253, 31, 204, 119, 209, 26, 243, 101, 7, 192, 66, 183, 193, 228, 91, 147, 155, 171, 148, 120, 128, 72, 239, 220, 42, 158, 31, 77, 95, 55, 24, 71, 240, 199, 104, 89, 246, 72, 100, 145, 186, 255, 247, 85, 241, 140, 168, 219, 209, 164, 228, 99, 152, 8, 162, 239, 91, 13, 96,
-    111, 55, 212, 219, 40, 74, 28, 107, 24, 149, 71, 139, 202, 136, 118, 238, 70, 199, 107, 231, 42, 131, 71, 70, 68, 188, 100, 145, 10, 112, 221, 79, 200, 120, 249, 188, 162, 1, 39, 181, 252, 92, 179, 217, 127, 139, 171, 64, 96, 212, 41, 134, 131, 189, 231, 4, 18, 171, 213, 246, 124, 49, 73, 6,
-    15, 19, 242, 98, 195, 149, 244, 170, 76, 188, 144, 195, 32, 127, 70, 173, 158, 32, 254, 226, 87, 244, 143, 49, 250, 231, 182, 78, 163, 29, 15, 29, 200, 71, 228, 164, 202, 87, 91, 191, 41, 52, 51, 25, 217, 123, 24, 57, 166, 31, 248, 140, 100, 198, 199, 147, 135, 37, 52, 213, 60, 246, 111, 5,
-    23, 38, 169, 143, 32, 23, 18, 251, 20, 147, 110, 240, 213, 27, 76, 153, 64, 73, 134, 254, 255, 15, 122, 36, 183, 56, 61, 76, 30, 174, 22, 20, 76, 150, 4, 7, 222, 81, 26, 30, 112, 70, 144, 20, 239, 207, 60, 49, 39, 179, 237, 111, 200, 181, 53, 81, 85, 236, 104, 194, 39, 56, 12, 173,
-    102, 188, 223, 74, 198, 30, 223, 208, 27, 219, 245, 40, 243, 172, 60, 7, 125, 246, 108, 158, 106, 21, 246, 3, 161, 234, 202, 22, 128, 130, 32, 171, 181, 28, 35, 70, 237, 85, 202, 99, 188, 91, 98, 122, 179, 74, 85, 137, 237, 95, 17, 92, 146, 120, 123, 215, 203, 125, 63, 198, 175, 123, 232, 223,
-    216, 161, 86, 6, 121, 154, 89, 97, 224, 51, 104, 224, 20, 44, 163, 192, 106, 217, 208, 124, 95, 73, 59, 102, 243, 183, 39, 128, 191, 76, 6, 225, 10, 30, 191, 36, 105, 164, 75, 169, 122, 16, 26, 47, 160, 116, 137, 110, 178, 53, 227, 94, 208, 221, 228, 55, 70, 26, 213, 178, 24, 114, 16, 116,
-    129, 163, 65, 165, 176, 237, 66, 202, 227, 170, 89, 122, 4, 63, 73, 115, 152, 30, 145, 139, 228, 94, 221, 216, 133, 128, 151, 95, 109, 128, 98, 214, 67, 98, 17, 233, 51, 229, 126, 252, 213, 176, 118, 167, 247, 106, 144, 14, 67, 145, 156, 167, 146, 152, 147, 137, 175, 196, 192, 63, 62, 152, 2, 156,
-    253, 163, 81, 205, 185, 31, 154, 153, 115, 56, 225, 161, 236, 254, 145, 231, 215, 159, 141, 217, 61, 156, 19, 120, 80, 133, 208, 126, 214, 120, 128, 160, 218, 100, 251, 202, 250, 101, 44, 48, 55, 146, 223, 133, 165, 230, 221, 214, 187, 14, 42, 70, 107, 220, 41, 151, 249, 153, 255, 12, 57, 75, 149, 207,
-    223, 85, 196, 172, 4, 81, 69, 99, 48, 187, 220, 84, 14, 113, 117, 94, 79, 139, 159, 249, 112, 88, 11, 242, 72, 102, 210, 246, 24, 122, 104, 73, 21, 223, 168, 212, 205, 173, 43, 88, 199, 166, 7, 31, 156, 149, 101, 168, 4, 163, 148, 109, 232, 111, 27, 157, 154, 217, 68, 225, 52, 252, 19, 241,
-    59, 50, 90, 73, 52, 52, 235, 108, 128, 124, 52, 144, 140, 164, 114, 94, 116, 149, 110, 169, 78, 11, 181, 171, 94, 83, 174, 206, 39, 139, 235, 235, 7, 116, 7, 80, 1, 255, 32, 94, 184, 188, 133, 112, 220, 155, 165, 91, 100, 184, 55, 131, 61, 140, 91, 206, 70, 132, 115, 188, 234, 70, 55, 143,
-    164, 234, 119, 62, 172, 116, 198, 147, 27, 69, 116, 69, 14, 62, 31, 228, 205, 118, 120, 112, 4, 207, 166, 80, 55, 70, 23, 29, 204, 159, 186, 84, 49, 102, 120, 245, 212, 89, 115, 169, 203, 192, 205, 10, 118, 106, 36, 230, 71, 198, 52, 111, 220, 110, 35, 58, 131, 172, 42, 158, 24, 158, 55, 217,
-};
-
-}
\ No newline at end of file
diff --git a/src/Math/Noise.hpp b/src/Math/Noise.hpp
deleted file mode 100644
index 80ceb47..0000000
--- a/src/Math/Noise.hpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include "Vector.hpp"
-
-#define NOISE_SEED_SIZE 64
-
-namespace Math {
-
-extern const uint8_t noise_seed[NOISE_SEED_SIZE][NOISE_SEED_SIZE];
-
-float noise2d(Vector<2> pos);
-
-}
\ No newline at end of file
diff --git a/src/Math/Perlin.cpp b/src/Math/Perlin.cpp
new file mode 100644
index 0000000..e0982cb
--- /dev/null
+++ b/src/Math/Perlin.cpp
@@ -0,0 +1,92 @@
+#include "Perlin.hpp"
+#include "Grid.hpp"
+#include "Interpolation.hpp"
+
+namespace Math::Perlin {
+
+float ease(float t) {
+    return t * t * t * ((6 * t - 15) * t + 10);
+}
+
+uint8_t hash(uint8_t x) {
+    auto rot = (x * 5) % 8;
+    return x << rot | x >> (8 - rot);
+}
+
+Vector<2> gradient(Vector<2> pos) {
+    Vector<2> gradients[] = {
+        {1.0f, 1.0f}, {-1.0f, 1.0f},
+        {1.0f, -1.0f}, {-1.0f, -1.0f},
+    };
+
+    auto x = hash(hash(pos.x()) + pos.y());
+    return gradients[x % 4].normalize();
+}
+
+float raw(Vector<2> pos) {
+    auto cell = grid_cell_for_point(pos);
+    auto uv = pos - cell.top_left();
+
+    auto unit = grid_cell_for_point({});
+    auto l11 = unit.top_left() - uv;
+    auto l21 = unit.top_right() - uv;
+    auto l12 = unit.bottom_left() - uv;
+    auto l22 = unit.bottom_right() - uv;
+
+    auto g11 = gradient(cell.top_left());
+    auto g21 = gradient(cell.top_right());
+    auto g12 = gradient(cell.bottom_left());
+    auto g22 = gradient(cell.bottom_right());
+
+    auto v = bilinear_interpolation(
+        {l11 * g11, l21 * g21, l12 * g12, l22 * g22},
+        cell,
+        uv.map(ease) + cell.top_left()
+    );
+    return (v + 1.0f) / 2.0f;
+}
+
+Vector<3> gradient(Vector<3> pos) {
+    constexpr float e = 0.5773502692;
+    static Vector<3> gradients[] = {
+        {e, e, e}, {-e, e, e}, {e, -e, e}, {-e, -e, e},
+        {e, e, -e}, {-e, e, -e}, {e, -e, -e}, {-e, -e, -e},
+    };
+
+    auto x = hash(hash(hash(pos.x()) + pos.y()) + pos.z());
+    return gradients[x % 8];
+}
+
+float raw(Vector<3> pos) {
+    auto cell = cube_cell_for_point(pos);
+    auto uv = pos - cell.front_top_left();
+
+    auto unit = cube_cell_for_point({});
+    auto l111 = unit.front_top_left() - uv;
+    auto l211 = unit.front_top_right() - uv;
+    auto l121 = unit.front_bottom_left() - uv;
+    auto l221 = unit.front_bottom_right() - uv;
+    auto l112 = unit.back_top_left() - uv;
+    auto l212 = unit.back_top_right() - uv;
+    auto l122 = unit.back_bottom_left() - uv;
+    auto l222 = unit.back_bottom_right() - uv;
+
+    auto g111 = gradient(cell.front_top_left());
+    auto g211 = gradient(cell.front_top_right());
+    auto g121 = gradient(cell.front_bottom_left());
+    auto g221 = gradient(cell.front_bottom_right());
+    auto g112 = gradient(cell.back_top_left());
+    auto g212 = gradient(cell.back_top_right());
+    auto g122 = gradient(cell.back_bottom_left());
+    auto g222 = gradient(cell.back_bottom_right());
+
+    auto v = trilinear_interpolation(
+        {l111 * g111, l211 * g211, l121 * g121, l221 * g221},
+        {l112 * g112, l212 * g212, l122 * g122, l222 * g222},
+        cell,
+        uv.map(ease) + cell.front_top_left()
+    );
+    return (v + 1.0f) / 2.0f;
+}
+
+}
\ No newline at end of file
diff --git a/src/Math/Perlin.hpp b/src/Math/Perlin.hpp
new file mode 100644
index 0000000..c31697b
--- /dev/null
+++ b/src/Math/Perlin.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "Common.hpp"
+
+namespace Math::Perlin {
+
+float raw(Vector<2> pos);
+float raw(Vector<3> pos);
+
+template <size_t D>
+struct Noise {
+    static_assert(D > 1 && D < 4);
+
+    Vector<D> offset{20000.0f};
+    float scale = 15.0f;
+
+    uint octaves = 3;
+    float persistence = 0.3f;
+
+    float at(Vector<D> pos) const {
+        float result = 0;
+        float max = 0;
+        float frequency = 1;
+        float amplitude = 1;
+
+        for (uint octave = 0; octave < octaves; octave++) {
+            result += raw((pos + offset).abs() / scale * frequency) * amplitude;
+            max += amplitude;
+
+            frequency *= 2;
+            amplitude *= persistence;
+        }
+
+        return result / max;
+    }
+};
+
+}
\ No newline at end of file
diff --git a/src/Math/Vector.hpp b/src/Math/Vector.hpp
index cb5eb24..9cc2d75 100644
--- a/src/Math/Vector.hpp
+++ b/src/Math/Vector.hpp
@@ -12,13 +12,17 @@ struct Vector {
 public:
     Vector(): elements{} {};
 
-    template<typename ...Args>
+    template<typename ...Args, typename std::enable_if<sizeof...(Args) == S, int>::type = 0>
     Vector<S, T>(Args... args) : elements{ args... } {};
 
     Vector<S, T>(T values[S]) {
         std::copy(values, values + S, elements);
     };
 
+    Vector<S, T>(T scalar) {
+        std::fill(elements, elements + S, scalar);
+    };
+
     Vector<S, T>(Vector<S - 1, T> vector, T scalar) {
         std::copy(vector.elements, vector.elements + S - 1, elements);
         elements[S - 1] = scalar;
@@ -61,6 +65,10 @@ public:
         return map([=](auto x) { return x / m; });
     }
 
+    Vector<S, T> abs() const {
+        return map([=](auto x) { return std::abs(x); });
+    }
+
     Vector<3, T> cross(Vector<3, T> other) const {
         return {
             y() * other.z() - z() * other.y(),
@@ -81,6 +89,10 @@ public:
         return map([&](auto i, auto x) { return x + other[i]; });
     }
 
+    Vector<S, T> operator+(T scalar) const {
+        return map([=](auto x) { return x + scalar; });
+    }
+
     Vector<S, T> operator*(T scalar) const {
         return map([=](auto x) { return x * scalar; });
     }
@@ -97,6 +109,10 @@ public:
         return map([](T x) -> T { return -x; });
     }
 
+    Vector<S, T> operator/(T scalar) const {
+        return map([=](auto x) { return x / scalar; });
+    }
+
     T x() const {
         static_assert(S > 0);
         return elements[0];
diff --git a/src/Util/ImageViewer.cpp b/src/Util/ImageViewer.cpp
index b4d8c6e..7fd5867 100644
--- a/src/Util/ImageViewer.cpp
+++ b/src/Util/ImageViewer.cpp
@@ -5,13 +5,14 @@
 namespace MC::Util {
 
 ImageViewer::ImageViewer(
-        GFX::Image::RawImage& image
+        GFX::Image::RawImage& image,
+        float window_aspect
 ) : m_texture(image),
     m_program(
         {GFX::Shading::Shader::Type::Vertex, ImageViewer::vertex},
         {GFX::Shading::Shader::Type::Fragment, ImageViewer::fragment}
     ),
-    m_mesh(GFX::Binder::load(default_mesh)) {
+    m_mesh(GFX::Binder::load(create_mesh(window_aspect, image.width(), image.height()))) {
     m_program.bind();
     auto model_uniform = m_program.uniform("model_matrix");
     auto view_uniform = m_program.uniform("view_matrix");
@@ -19,7 +20,7 @@ ImageViewer::ImageViewer(
 
     model_uniform.set(Math::MVP::model({}, {}));
     view_uniform.set(Math::MVP::view({}, {}));
-    projection_uniform.set(Math::MVP::orthographic_projection(1000, 800, 0.0f, 100.0f));
+    projection_uniform.set(Math::MVP::orthographic_projection(1000 * window_aspect, 1000, 0.0f, 100.0f));
 
     m_program.unbind();
 }
@@ -34,19 +35,27 @@ void ImageViewer::render() {
     m_program.unbind();
 }
 
-GFX::Mesh ImageViewer::create_default_mesh() {
+GFX::Mesh ImageViewer::create_mesh(float window_aspect, uint32_t image_width, uint32_t image_height) {
+    auto aspect = (float)image_width / image_height;
+    float max_size = 500.0f;
+    float width = max_size * std::min(1.0f, aspect);
+    float height = max_size * std::min(1.0f, 1/aspect);
+
+    float x = max_size * window_aspect - width / 2.0f;
+    float y = max_size - height / 2.0f;
+
     return {{
         std::vector<Vector<3>>{
-            {300.0f, 200.0f,   0.0f},   // top left
-            {300.0f, 600.0f, 0.0f},   // bottom left
-            {700.0f, 600.0f, 0.0f},   // bottom right
-            {700.0f, 200.0f,   0.0f } // top right
+            {x, y, 0.0f}, // top left
+            {x, y + height, 0.0f}, // bottom left
+            {x + width, y + height, 0.0f}, // bottom right
+            {x + width, y, 0.0f}  // top right
         },
         std::vector<Vector<2>>{
-            {1.0f, 1.0f},
-            {1.0f, 0.0f,},
             {0.0f, 0.0f},
             {0.0f, 1.0f},
+            {1.0f, 1.0f},
+            {1.0f, 0.0f},
         },
     }, {0, 1, 2, 0, 2, 3}};
 }
diff --git a/src/Util/ImageViewer.hpp b/src/Util/ImageViewer.hpp
index cb2dda0..2e4efe3 100644
--- a/src/Util/ImageViewer.hpp
+++ b/src/Util/ImageViewer.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <ostream>
 #include "../GFX/Image/RawImage.hpp"
 #include "../GFX/Binder.hpp"
 #include "../GFX/Texture.hpp"
@@ -9,13 +10,11 @@ namespace MC::Util {
 
 class ImageViewer {
 public:
-    explicit ImageViewer(GFX::Image::RawImage& image);
+    explicit ImageViewer(GFX::Image::RawImage& image, float window_aspect);
 
     void render();
 private:
-    static GFX::Mesh create_default_mesh();
-
-    static inline GFX::Mesh default_mesh = create_default_mesh();
+    static GFX::Mesh create_mesh(float window_aspect, uint32_t image_width, uint32_t image_height);
 
     static const char* vertex;
     static const char* fragment;
diff --git a/src/Util/Sampler.hpp b/src/Util/Sampler.hpp
new file mode 100644
index 0000000..3a4ff4f
--- /dev/null
+++ b/src/Util/Sampler.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <functional>
+#include "../Math/Vector.hpp"
+#include "../Math/Grid.hpp"
+
+namespace MC::Util {
+
+template<size_t D, typename T>
+class Sampler {
+    using Pos = Vector<D>;
+    using Sample = std::function<T(Pos)>;
+    using Interpolate = std::function<T(Pos, Sample)>;
+public:
+    Sampler(
+        Sample sample,
+        Pos offset = {},
+        float scale = 1.0f,
+        Interpolate interpolate = nearest_interpolation
+    ) : m_sample([=](Sampler::Pos pos) -> T {
+        return interpolate(pos, [=](Sampler::Pos p) -> T { return sample(p * scale + offset); });
+    }) {}
+
+    T sample(Pos at) {
+        return m_sample(at);
+    }
+
+    static T nearest_interpolation(Pos pos, Sample sample) {
+        return sample(pos);
+    }
+
+private:
+    Sample m_sample;
+};
+
+}
\ No newline at end of file
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;
diff --git a/src/main.cpp b/src/main.cpp
index dcac677..b407a78 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -10,7 +10,6 @@
 #include "GFX/Texture.hpp"
 #include "GFX/Image/PPMParser.hpp"
 #include "World/World.hpp"
-#include "Util/ImageViewer.hpp"
 
 #define APP_NAME "Meowcraft"
 
@@ -66,7 +65,6 @@ void run() {
     auto model_uniform = program.uniform("model_matrix");
     auto view_uniform = program.uniform("view_matrix");
     auto projection_uniform = program.uniform("projection_matrix");
-
     auto sun_direction_uniform = program.uniform("sun_direction");
 
     program.bind();
@@ -101,7 +99,7 @@ void run() {
         glClearColor(0.85f, 0.85f, 0.85f, 1.0f); // #DBDBDB
         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
         for (auto& chunk : world.get_visible_chunks(camera.position())) {
-            auto model = Math::MVP::model(chunk.chunk->position(), {});
+            auto model = Math::MVP::model(chunk.chunk.value().position(), {});
             model_uniform.set(model);
             render(chunk.mesh.value(), texture);
         }
@@ -130,11 +128,13 @@ void process_input(MC::GFX::Window& window, MC::GFX::Camera& camera) {
     float x = key(GLFW_KEY_D) - key(GLFW_KEY_A);
     float y = key(GLFW_KEY_SPACE) - key(GLFW_KEY_LEFT_SHIFT);
     float z = key(GLFW_KEY_S) - key(GLFW_KEY_W);
+    float boost = key(GLFW_KEY_LEFT_CONTROL) * 2.0f;
 
-    auto move_speed = 0.2f;
+    auto move_speed = 0.2f + boost;
     auto rotation_speed = 0.1f;
 
-    camera.move_relative({x * move_speed, y * move_speed, z * move_speed});
+    camera.move_relative({x * move_speed, 0.0f, z * move_speed});
+    camera.move({0.0f, y * move_speed, 0.0f});
     camera.rotate({r.y() * rotation_speed, r.x() * rotation_speed, 0.0f});
 }