summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/GFX/Util/MeshBuilder.hpp13
-rw-r--r--src/GFX/Util/Primitives.cpp107
-rw-r--r--src/GFX/Util/Primitives.hpp47
-rw-r--r--src/Math/AABB.hpp10
-rw-r--r--src/World/Clouds.cpp74
-rw-r--r--src/World/Clouds.hpp10
-rw-r--r--src/main.cpp2
7 files changed, 241 insertions, 22 deletions
diff --git a/src/GFX/Util/MeshBuilder.hpp b/src/GFX/Util/MeshBuilder.hpp
index e3caa8c..c174b8c 100644
--- a/src/GFX/Util/MeshBuilder.hpp
+++ b/src/GFX/Util/MeshBuilder.hpp
@@ -2,6 +2,7 @@
 
 #include "../../Common/Sizes.hpp"
 #include "../../Math/Common.hpp"
+#include "Primitives.hpp"
 #include <vector>
 
 namespace MC::GFX::Util {
@@ -53,6 +54,18 @@ public:
         m_indices.insert(m_indices.end(), from.begin(), from.end());
     }
 
+    template<uint PN>
+    void primitive(const Primitives::Primitive<PN>& primitive) {
+        decltype(primitive.indices) relativized_indices{};
+        for (Int i = 0; i < primitive.indices.size(); i++) {
+            relativized_indices[i] = primitive.indices[i] + m_positions.size();
+        }
+
+        positions(primitive.positions);
+        attributes<0>(primitive.normals);
+        indices(relativized_indices);
+    }
+
     Mesh mesh() {
         return mesh(std::make_index_sequence<AttributesN>{});
     }
diff --git a/src/GFX/Util/Primitives.cpp b/src/GFX/Util/Primitives.cpp
new file mode 100644
index 0000000..d2c3690
--- /dev/null
+++ b/src/GFX/Util/Primitives.cpp
@@ -0,0 +1,107 @@
+#include "Primitives.hpp"
+
+namespace MC::GFX::Util::Primitives {
+
+PlanePrimitive plane(Math::AABB aabb, FaceSet face) {
+    decltype(PlanePrimitive::positions) positions;
+    auto [min, max] = aabb;
+
+    switch (face) {
+    case FaceSet::Front:
+        positions = {{
+            {min.x(), min.y(), min.z()}, {min.x(), max.y(), min.z()},
+            {max.x(), max.y(), min.z()}, {max.x(), min.y(), min.z()}
+        }};
+        break;
+    case FaceSet::Back:
+        positions = {{
+            {min.x(), min.y(), max.z()}, {max.x(), min.y(), max.z()},
+            {max.x(), max.y(), max.z()}, {min.x(), max.y(), max.z()}
+        }};
+        break;
+    case FaceSet::Top:
+        positions = {{
+            {min.x(), max.y(), min.z()}, {min.x(), max.y(), max.z()},
+            {max.x(), max.y(), max.z()}, {max.x(), max.y(), min.z()}
+        }};
+        break;
+    case FaceSet::Bottom:
+        positions = {{
+            {min.x(), min.y(), min.z()}, {max.x(), min.y(), min.z()},
+            {max.x(), min.y(), max.z()}, {min.x(), min.y(), max.z()}
+        }};
+        break;
+    case FaceSet::Right:
+        positions = {{
+            {max.x(), min.y(), min.z()}, {max.x(), max.y(), min.z()},
+            {max.x(), max.y(), max.z()}, {max.x(), min.y(), max.z()}
+        }};
+        break;
+    case FaceSet::Left:
+        positions = {{
+            {min.x(), min.y(), min.z()}, {min.x(), min.y(), max.z()},
+            {min.x(), max.y(), max.z()}, {min.x(), max.y(), min.z()}
+        }};
+        break;
+    }
+
+    auto is_side = [=](FaceSet f) -> I8 { return f == face; };
+    Vector<3, F32> normal = {
+        is_side(FaceSet::Right) - is_side(FaceSet::Left),
+        is_side(FaceSet::Top) - is_side(FaceSet::Bottom),
+        is_side(FaceSet::Front) - is_side(FaceSet::Back),
+    };
+
+    return {positions, {normal, normal, normal, normal}, {0, 1, 2, 2, 3, 0}};
+}
+
+BoxPrimitive box(Math::AABB aabb, FaceSet faces) {
+    BoxPrimitive box{};
+    auto [min, max] = aabb;
+
+    UInt set_faces = 0;
+    U8 face_value = 1;
+    for (UInt face_index = 0; face_index < FaceSet::Size; face_index++, face_value <<= 1) {
+        FaceSet face = (FaceSet::Value)face_value;
+        if ((faces & face) == 0) continue;
+
+        Math::AABB face_aabb;
+        switch (face) {
+        case FaceSet::Front:
+            face_aabb = {min, {max.x(), max.y(), min.z()}};
+            break;
+        case FaceSet::Back:
+            face_aabb = {{min.x(), min.y(), max.z()}, max};
+            break;
+        case FaceSet::Top:
+            face_aabb = {{min.x(), max.y(), min.z()}, {max.x(), max.y(), max.z()}};
+            break;
+        case FaceSet::Bottom:
+            face_aabb = {min, {max.x(), min.y(), max.z()}};
+            break;
+        case FaceSet::Right:
+            face_aabb = {{max.x(), min.y(), min.z()}, max};
+            break;
+        case FaceSet::Left:
+            face_aabb = {min, {min.x(), max.y(), max.z()}};
+            break;
+        }
+
+        auto p = plane(face_aabb, face);
+
+        for (UInt i = 0; i < p.positions.size(); i++) {
+            box.positions[set_faces * 4 + i] = p.positions[i];
+        }
+        for (UInt i = 0; i < p.normals.size(); i++) {
+            box.normals[set_faces * 4 + i] = p.normals[i];
+        }
+        for (UInt i = 0; i < p.indices.size(); i++) {
+            box.indices[set_faces * 6 + i] = p.indices[i] + set_faces * 4;
+        }
+        set_faces++;
+    }
+
+    return box;
+}
+
+}
\ No newline at end of file
diff --git a/src/GFX/Util/Primitives.hpp b/src/GFX/Util/Primitives.hpp
new file mode 100644
index 0000000..b96bc00
--- /dev/null
+++ b/src/GFX/Util/Primitives.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "../../Common/Sizes.hpp"
+#include "../../Math/AABB.hpp"
+#include <array>
+
+namespace MC::GFX::Util::Primitives {
+
+class FaceSet {
+public:
+    enum Value : U8 {
+        Front = 1 << 0,
+        Back = 1 << 1,
+        Top = 1 << 2,
+        Bottom = 1 << 3,
+        Right = 1 << 4,
+        Left = 1 << 5,
+    };
+
+    static constexpr UInt Size = 6;
+
+    FaceSet() = default;
+    FaceSet(const Value set) : m_set(set) {}
+
+    operator Value() const { return m_set; }
+
+    static FaceSet all() {
+        return static_cast<Value>(Front | Back | Top | Bottom | Right | Left);
+    }
+private:
+    Value m_set;
+};
+
+template <uint N>
+struct Primitive {
+    std::array<Vector<3, F32>, N> positions;
+    std::array<Vector<3, F32>, N> normals;
+    std::array<U32, N + N / 2> indices;
+};
+
+using PlanePrimitive = Primitive<4>;
+PlanePrimitive plane(Math::AABB aabb, FaceSet face);
+
+using BoxPrimitive = Primitive<4 * 6>;
+BoxPrimitive box(Math::AABB aabb, FaceSet faces = FaceSet::all());
+
+}
diff --git a/src/Math/AABB.hpp b/src/Math/AABB.hpp
new file mode 100644
index 0000000..2c02abf
--- /dev/null
+++ b/src/Math/AABB.hpp
@@ -0,0 +1,10 @@
+#pragma once
+#include "Vector.hpp"
+
+namespace Math {
+
+struct AABB {
+    Vector<3> min, max;
+};
+
+}
diff --git a/src/World/Clouds.cpp b/src/World/Clouds.cpp
index 6c5dba3..36f2441 100644
--- a/src/World/Clouds.cpp
+++ b/src/World/Clouds.cpp
@@ -1,15 +1,16 @@
 #include "Clouds.hpp"
 #include "../Math/MVP.hpp"
 #include "../Math/Perlin.hpp"
+#include "../Math/AABB.hpp"
+#include "../GFX/Util/Primitives.hpp"
 #include "../GFX/Util/MeshBuilder.hpp"
 #include <GL/glew.h>
 #include <array>
 
 namespace MC::World {
 
-Clouds::Clouds(Real ascept, Real fov, Real near, Real far, Vector<3, F32> sky_color)
-    : m_position(-1000, 120, -1000),
-    m_program(
+Clouds::Clouds(Real ascept, Real fov, Real near, Real far, Vector<3, F32> sky_color, Vector<3, F32> sun_direction)
+    : m_program(
     {GFX::Shading::Shader::Type::Vertex, vertex},
     {GFX::Shading::Shader::Type::Fragment, fragment}
     ),
@@ -21,24 +22,41 @@ Clouds::Clouds(Real ascept, Real fov, Real near, Real far, Vector<3, F32> sky_co
     m_view_uniform = m_program.uniform("view_matrix");
     m_projection_uniform = m_program.uniform("projection_matrix");
     auto sky_color_uniform = m_program.uniform("sky_color");
+    auto sun_direction_uniform = m_program.uniform("sun_direction");
 
     m_model_uniform.set(Math::MVP::model<F32>({}, {}, {}));
     m_view_uniform.set(Math::MVP::view<F32>({}, {}));
     m_projection_uniform.set(Math::MVP::perspective_projection<F32>(ascept, fov, near, far));
     sky_color_uniform.set(sky_color);
+    sun_direction_uniform.set(sun_direction);
 
     m_program.unbind();
 }
 
 void Clouds::update(U64 time) {
-    m_position.x() += time / 5000.0;
+    m_x_offset += time / 5000.0;
 }
 
 void Clouds::render(const GFX::Camera& camera) const {
+    auto position = camera.position();
+
+    I32 center_x = std::round((position.x() - m_x_offset) / TileSize);
+    I32 center_y = std::round(position.z() / TileSize);
+
+    for (Int x = -1; x <= 1; x++) {
+        for (Int y = -1; y <= 1; y++) {
+            render_single_instance(camera, center_x + x, center_y + y);
+        }
+    }
+}
+
+void Clouds::render_single_instance(const GFX::Camera& camera, Int x, Int y) const {
+    Vector<3> position{TileSize * x + m_x_offset, Height, TileSize * y};
+
     m_program.bind();
 
     m_view_uniform.set(Math::MVP::view<F32>(camera.position(), camera.angles()));
-    m_model_uniform.set(Math::MVP::model<F32>(m_position, Vector<3>{10.0}, {}));
+    m_model_uniform.set(Math::MVP::model<F32>(position, Vector<3>{Scale}, {}));
 
     m_mesh.bind();
     glDrawElements(GL_TRIANGLES, m_mesh.size(), GL_UNSIGNED_INT, nullptr);
@@ -51,8 +69,8 @@ Clouds::CloudMatrix Clouds::create_cloud_matrix() {
     Math::Perlin::Noise<2> noise{};
 
     CloudMatrix clouds{false};
-    for (Int x = 0; x < CloudMatrixSize; x++) {
-        for (Int y = 0; y < CloudMatrixSize; y++) {
+    for (Int x = 1; x < CloudMatrixSize; x++) {
+        for (Int y = 1; y < CloudMatrixSize; y++) {
             if (noise.at({x, y * 2}) > 0.55) {
                 clouds(x, y) = true;
             }
@@ -62,20 +80,31 @@ Clouds::CloudMatrix Clouds::create_cloud_matrix() {
 }
 
 GFX::Mesh Clouds::create_mesh(const CloudMatrix& cloud_matrix) {
-    GFX::Util::MeshBuilder builder{};
+    GFX::Util::MeshBuilder<Vector<3, F32>> builder{};
 
     for (Int x = 0; x < CloudMatrixSize; x++) {
         for (Int y = 0; y < CloudMatrixSize; y++) {
             if (!cloud_matrix(x, y)) continue;
 
-            U32 s = builder.vertex_count();
-            std::array<Vector<3, F32>, 4> positions = {{{0, 0, 0}, {1, 0, 0}, {0, 0, 1}, {1, 0, 1}}};
-            std::array<U32, 6> indices = { s + 0, s + 1, s + 2, s + 1, s + 3, s + 2 };
-
-            for (auto& p : positions) p += {x, 0, y};
-
-            builder.positions(positions);
-            builder.indices(indices);
+            auto is_occupied = [&](Int xn, Int yn) {
+                if (xn < 0 || xn >= CloudMatrixSize || yn < 0 || yn >= CloudMatrixSize) return false;
+                return cloud_matrix(xn, yn);
+            };
+            std::array<bool, 4> neighbors = {
+                is_occupied(x - 1, y), is_occupied(x + 1, y),
+                is_occupied(x, y - 1), is_occupied(x, y + 1),
+            };
+
+            using FaceSet = GFX::Util::Primitives::FaceSet;
+            U8 faces = FaceSet::Top | FaceSet::Bottom;
+            if (!neighbors[0]) faces |= FaceSet::Left;
+            if (!neighbors[1]) faces |= FaceSet::Right;
+            if (!neighbors[2]) faces |= FaceSet::Front;
+            if (!neighbors[3]) faces |= FaceSet::Back;
+
+            auto aabb = Math::AABB{{x, 0, y}, {x + 1, 1, y + 1}};
+            auto box = GFX::Util::Primitives::box(aabb, (FaceSet::Value)faces);
+            builder.primitive(box);
         }
     }
 
@@ -90,7 +119,9 @@ uniform mat4 view_matrix;
 uniform mat4 projection_matrix;
 
 layout (location = 0) in vec3 position;
+layout (location = 1) in vec3 normal;
 
+out vec3 surface_normal;
 out float depth;
 
 void main() {
@@ -99,7 +130,8 @@ void main() {
     vec4 clip_position = projection_matrix * view_position;
 
     gl_Position = clip_position;
-    depth = clamp((length(view_position) - 75) / 125, 0.0, 1.0);
+    surface_normal = (model_matrix * vec4(normal, 0.0)).xyz;
+    depth = clamp((length(view_position) - 200) / 400, 0.0, 1.0);
 }
 )v";
 
@@ -107,14 +139,18 @@ const Char* Clouds::fragment = R"f(
 #version 330 core
 
 uniform vec3 sky_color;
+uniform vec3 sun_direction;
 
+in vec3 surface_normal;
 in float depth;
 out vec4 color;
 
 void main() {
-    vec3 white = vec3(1.0, 1.0, 1.0);
+    float brightness = dot(normalize(surface_normal), normalize(-sun_direction));
+    vec3 diffuse = vec3(1 - clamp(brightness, -0.3, 0.2));
+    vec3 base = vec3(1.0, 1.0, 1.0) * diffuse;
 
-    color = vec4(mix(sky_color, white, 1 - depth), 0.5);
+    color = vec4(mix(sky_color, base, 1 - depth), 0.3);
 }
 )f";
 
diff --git a/src/World/Clouds.hpp b/src/World/Clouds.hpp
index 6a91901..b2d5a10 100644
--- a/src/World/Clouds.hpp
+++ b/src/World/Clouds.hpp
@@ -9,21 +9,27 @@ namespace MC::World {
 
 class Clouds {
 public:
-    Clouds(Real ascept, Real fov, Real near, Real far, Vector<3, F32> sky_color);
+    Clouds(Real ascept, Real fov, Real near, Real far, Vector<3, F32> sky_color, Vector<3, F32> sun_direction);
 
     void update(U64 time);
     void render(const GFX::Camera& camera) const;
 private:
     constexpr static U32 CloudMatrixSize = 128;
+    constexpr static Int Height = 200;
+    constexpr static Real Scale = 15;
+    constexpr static Real TileSize = CloudMatrixSize * Scale;
+
     using CloudMatrix = Matrix<CloudMatrixSize, CloudMatrixSize, Bool>;
 
+    void render_single_instance(const GFX::Camera& camera, Int x, Int y) const;
+
     static CloudMatrix create_cloud_matrix();
     static GFX::Mesh create_mesh(const CloudMatrix& cloud_matrix);
 
     static const Char* vertex;
     static const Char* fragment;
 
-    Vector<3> m_position;
+    Real m_x_offset = 0.0;
 
     GFX::Shading::Program m_program;
     GFX::BindableMesh m_mesh;
diff --git a/src/main.cpp b/src/main.cpp
index 1688dac..6e78dab 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -81,7 +81,7 @@ void run() {
     Vector<3, F32> sky_color{0.85, 0.85, 0.85}; // #DBDBDB
     sky_color_uniform.set(sky_color);
 
-    MC::World::Clouds clouds{ASPECT, FOV, 0.1f, 1000.0f, sky_color};
+    MC::World::Clouds clouds{ASPECT, FOV, 0.1f, 1000.0f, sky_color, sun_direction};
 
     glEnable(GL_DEPTH_TEST);
     glDepthFunc(GL_LEQUAL);