summary refs log tree commit diff
path: root/src/World/Clouds.cpp
blob: 2567985b41f37e6307bd56a399f29d6416938c91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#include "Clouds.hpp"
#include "../Time.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, Vector<3, F32> sun_direction)
    : m_program(
    {GFX::Shading::Shader::Type::Vertex, vertex},
    {GFX::Shading::Shader::Type::Fragment, fragment}
    ),
    m_mesh(create_mesh(create_cloud_matrix())),
    m_model_uniform(), m_view_uniform(), m_projection_uniform() {
    m_program.bind();

    m_model_uniform = m_program.uniform("model_matrix");
    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(const Time& time) {
    m_x_offset += 5.0 * time.delta();
}

void Clouds::render(const GFX::Camera& camera) {
    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) {
    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>(position, Vector<3>{Scale}, {}));

    m_mesh.bind();
    glDrawElements(GL_TRIANGLES, m_mesh.size(), GL_UNSIGNED_INT, nullptr);
    m_mesh.unbind();

    m_program.unbind();
}

Clouds::CloudMatrix Clouds::create_cloud_matrix() {
    Math::Perlin::Noise<2> noise{};

    CloudMatrix clouds{false};
    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;
            }
        }
    }
    return clouds;
}

GFX::Mesh Clouds::create_mesh(const CloudMatrix& cloud_matrix) {
    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;

            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);
        }
    }

    return builder.mesh();
}

const Char* Clouds::vertex = R"v(
#version 330 core

uniform mat4 model_matrix;
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() {
    vec4 world_position = model_matrix * vec4(position, 1.0);
    vec4 view_position = view_matrix * world_position;
    vec4 clip_position = projection_matrix * view_position;

    gl_Position = clip_position;
    surface_normal = (model_matrix * vec4(normal, 0.0)).xyz;
    depth = clamp((length(view_position) - 200) / 400, 0.0, 1.0);
}
)v";

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() {
    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, base, 1 - depth), 0.3);
}
)f";

}