summaryrefslogtreecommitdiff
path: root/game/terrain.cpp
blob: 27fdc1c2c7fdd5d4647485ecce2f67015dc266fd (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
#include "terrain.h"
#include "gfx/models/texture.h"
#include <array>
#include <cache.h>
#include <cmath>
#include <cstddef>
#include <gfx/gl/shader.h>
#include <gfx/gl/transform.h>
#include <gfx/models/vertex.hpp>
#include <glm/glm.hpp>
#include <random>

constexpr auto size {241}; // Vertices
constexpr auto offset {(size - 1) / 2};
constexpr auto verticesCount = size * size;
constexpr auto tilesCount = (size - 1) * (size - 1);
constexpr auto trianglesCount = tilesCount * 2;
constexpr auto indicesCount = trianglesCount * 3;
constexpr auto resolution = 10; // Grid size
constexpr auto extent = offset * resolution;

Terrain::Terrain() :
	m_vertexArrayObject {}, m_vertexArrayBuffers {}, texture {Texture::cachedTexture.get("res/terrain.png")}
{
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	vertices.reserve(verticesCount + 4);
	vertices.resize(verticesCount, {{}, {}, {}});
	indices.reserve(indicesCount + 6);

	// Initial coordinates
	for (auto z = 0; z < size; z += 1) {
		for (auto x = 0; x < size; x += 1) {
			auto & vertex = vertices[x + (z * size)];
			vertex.pos = {resolution * (x - offset), -1.5, resolution * (z - offset)};
			vertex.normal = {0, 1, 0};
			vertex.texCoord = {(x % 2) / 2.01, (z % 2) / 2.01};
		}
	}
	// Indices
	for (auto z = 0; z < size - 1; z += 1) {
		for (auto x = 0; x < size - 1; x += 1) {
			indices.push_back(x + (z * size));
			indices.push_back((x + 1) + ((z + 1) * size));
			indices.push_back((x + 1) + (z * size));
			indices.push_back(x + (z * size));
			indices.push_back(x + ((z + 1) * size));
			indices.push_back((x + 1) + ((z + 1) * size));
		}
	}
	// Add hills
	std::mt19937 gen(std::random_device {}());
	std::uniform_int_distribution<> rpos(2, size - 2);
	std::uniform_int_distribution<> rsize(10, 20);
	std::uniform_int_distribution<> rheight(1, 3);
	for (int h = 0; h < 500;) {
		const glm::ivec2 hpos {rpos(gen), rpos(gen)};
		const glm::ivec2 hsize {rsize(gen), rsize(gen)};
		if (const auto lim1 = hpos - hsize; lim1.x > 0 && lim1.y > 0) {
			if (const auto lim2 = hpos + hsize; lim2.x < size && lim2.y < size) {
				auto height {rheight(gen)};
				const glm::ivec2 hsizesqrd {hsize.x * hsize.x, hsize.y * hsize.y};
				for (auto z = lim1.y; z < lim2.y; z += 1) {
					for (auto x = lim1.x; x < lim2.x; x += 1) {
						const auto dist {hpos - glm::ivec2 {x, z}};
						const glm::ivec2 distsqrd {dist.x * dist.x, dist.y * dist.y};
						if ((pow(x - hpos.x, 2) / pow(hsize.x, 2)) + (pow(z - hpos.y, 2) / pow(hsize.y, 2)) <= 1.0) {
							auto & vertex = vertices[x + (z * size)];
							vertex.pos.y += height;
						}
					}
				}
				h += 1;
			}
		}
	}
	// Normals
	for (auto z = 1; z < size - 1; z += 1) {
		for (auto x = 1; x < size - 1; x += 1) {
			const auto a = v(x - 1, z).pos;
			const auto b = v(x, z - 1).pos;
			const auto c = v(x + 1, z).pos;
			const auto d = v(x, z + 1).pos;
			v(x, z).normal = -glm::normalize(glm::cross(c - a, d - b));
		}
	}
	// Add water
	vertices.emplace_back(glm::vec3 {-extent, 0, -extent}, glm::vec2 {0.5, 0.0}, glm::vec3 {0, 1, 0});
	vertices.emplace_back(glm::vec3 {-extent, 0, extent}, glm::vec2 {0.5, 0.5}, glm::vec3 {0, 1, 0});
	vertices.emplace_back(glm::vec3 {extent, 0, extent}, glm::vec2 {1, 0.5}, glm::vec3 {0, 1, 0});
	vertices.emplace_back(glm::vec3 {extent, 0, -extent}, glm::vec2 {1, 0.0}, glm::vec3 {0, 1, 0});
	indices.push_back(verticesCount);
	indices.push_back(verticesCount + 1);
	indices.push_back(verticesCount + 2);
	indices.push_back(verticesCount);
	indices.push_back(verticesCount + 2);
	indices.push_back(verticesCount + 3);

	glGenVertexArrays(1, &m_vertexArrayObject);
	glBindVertexArray(m_vertexArrayObject);

	glGenBuffers(2, m_vertexArrayBuffers.data());

	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);

	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, pos));

	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, texCoord));

	glEnableVertexAttribArray(2);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, normal));

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vertexArrayBuffers[1]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), indices.data(), GL_STATIC_DRAW);

	glBindVertexArray(0);
}

Vertex &
Terrain::v(unsigned int x, unsigned int z)
{
	return vertices[x + (z * size)];
}

Terrain::~Terrain()
{
	glDeleteBuffers(NUM_BUFFERS, m_vertexArrayBuffers.data());
	glDeleteVertexArrays(1, &m_vertexArrayObject);
}

static const Transform identity {};
static const auto identityModel {identity.GetModel()};

void
Terrain::render(const Shader & shader) const
{
	shader.setModel(identityModel);
	texture->Bind();
	glBindVertexArray(m_vertexArrayObject);

	glDrawElementsBaseVertex(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, nullptr, 0);

	glBindVertexArray(0);
}