]> git.localhorst.tv Git - tacos.git/blob - src/world/world.cpp
eba12eab25ef4c334b329e0b50081c4ca2953ae1
[tacos.git] / src / world / world.cpp
1 #include "Floor.hpp"
2
3
4 namespace tacos {
5
6 constexpr int Floor::VAO_DIVISOR;
7
8 Floor::Floor(int w, int d)
9 : width(w)
10 , depth(d)
11 , elevation(w * d)
12 , tile_width(width - 1)
13 , tile_depth(depth - 1)
14 , unclean_width(tile_width % VAO_DIVISOR)
15 , unclean_depth(tile_depth % VAO_DIVISOR)
16 , vao_width(tile_width / VAO_DIVISOR + bool(unclean_width))
17 , vao_depth(tile_depth / VAO_DIVISOR + bool(unclean_depth))
18 , vaos(vao_width * vao_depth)
19 , general_elements(vao_width * vao_depth)
20 , unclean_width_elements(general_elements + bool(unclean_width))
21 , unclean_depth_elements(unclean_depth ? (unclean_width_elements + 1) : general_elements)
22 , unclean_corner_elements(std::max(unclean_width_elements, unclean_depth_elements) + (unclean_width && unclean_depth))
23 , buffers(unclean_corner_elements + 1) {
24         glGenVertexArrays(vaos.size(), vaos.data());
25         glGenBuffers(buffers.size(), buffers.data());
26         int x_end = vao_width - bool(unclean_width);
27         int z_end = vao_depth - bool(unclean_depth);
28         for (int z = 0; z < z_end; ++z) {
29                 for (int x = 0; x < x_end; ++x) {
30                         SetupVAO(z * vao_width + x, buffers[general_elements], (VAO_DIVISOR + 1) * (VAO_DIVISOR + 1));
31                 }
32         }
33         // always fill general element buffer (there won't be one needed for maps with x or z less than divisor,
34         // but that shouldn't happen in practice, only during tests in which case I don't care about the overhead
35         FillElementBuffer(buffers[general_elements], VAO_DIVISOR, VAO_DIVISOR);
36         if (unclean_width) {
37                 for (int z = 0; z < z_end; ++z) {
38                         SetupVAO(z * vao_width + x_end, buffers[unclean_width_elements], (unclean_width + 1) * (VAO_DIVISOR + 1));
39                 }
40                 FillElementBuffer(buffers[unclean_width_elements], unclean_width, VAO_DIVISOR);
41         }
42         if (unclean_depth) {
43                 for (int x = 0; x < x_end; ++x) {
44                         SetupVAO(z_end * vao_width + x, buffers[unclean_depth_elements], (VAO_DIVISOR + 1) * (unclean_depth + 1));
45                 }
46                 FillElementBuffer(buffers[unclean_depth_elements], VAO_DIVISOR, unclean_depth);
47         }
48         if (unclean_width && unclean_depth) {
49                 SetupVAO(z_end * vao_width + x_end, buffers[unclean_corner_elements], (unclean_width + 1) * (unclean_depth + 1));
50                 FillElementBuffer(buffers[unclean_corner_elements], unclean_width, unclean_depth);
51         }
52 }
53
54 Floor::~Floor() noexcept {
55         glDeleteBuffers(buffers.size(), buffers.data());
56         glDeleteVertexArrays(vaos.size(), vaos.data());
57 }
58
59 void Floor::SetupVAO(int which, GLuint element_buffer, int vertex_count) noexcept {
60         glBindVertexArray(vaos[which]);
61         glBindBuffer(GL_ARRAY_BUFFER, buffers[which]);
62         glBufferData(GL_ARRAY_BUFFER, vertex_count * sizeof(Attributes), nullptr, GL_STATIC_DRAW);
63         glEnableVertexAttribArray(0);
64         glVertexAttribPointer(0, 3, GL_FLOAT, 0, sizeof(Attributes), reinterpret_cast<const void *>(offsetof(Attributes, position)));
65         glEnableVertexAttribArray(1);
66         glVertexAttribPointer(1, 3, GL_FLOAT, 0, sizeof(Attributes), reinterpret_cast<const void *>(offsetof(Attributes, normal)));
67         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);
68 }
69
70 void Floor::FillElementBuffer(GLuint which, int tile_width, int tile_depth) {
71         // unbind VAO so we don't accidentally trash an element buffer binding
72         glBindVertexArray(0);
73         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, which);
74         glBufferData(GL_ELEMENT_ARRAY_BUFFER, tile_width * tile_depth * sizeof(short) * 6, nullptr, GL_STATIC_DRAW);
75         // TODO: this can return null on error (out of memory in this case)
76         //       might be worth checking eventually
77         short *data = reinterpret_cast<short *>(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY));
78         for (int z = 0, i = 0; z < tile_depth; ++z) {
79                 for (int x = 0; x < tile_width; ++x, ++i) {
80                         data[i * 6 + 0] = (z + 0) * (tile_width + 1) + (x + 0);
81                         data[i * 6 + 1] = (z + 0) * (tile_width + 1) + (x + 1);
82                         data[i * 6 + 2] = (z + 1) * (tile_width + 1) + (x + 0);
83                         data[i * 6 + 3] = (z + 0) * (tile_width + 1) + (x + 1);
84                         data[i * 6 + 4] = (z + 1) * (tile_width + 1) + (x + 1);
85                         data[i * 6 + 5] = (z + 1) * (tile_width + 1) + (x + 0);
86                 }
87         }
88         glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
89 }
90
91 void Floor::FillAttribBuffer(int vao_x, int vao_z) {
92         glBindBuffer(GL_ARRAY_BUFFER, buffers[vao_z * vao_width + vao_x]);
93         Attributes *data = reinterpret_cast<Attributes *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
94         glm::ivec2 tiles(Tiles(vao_x, vao_z));
95         for (int z = 0, abs_z = vao_z * VAO_DIVISOR, i = 0; z < tiles.y + 1; ++z, ++abs_z) {
96                 for (int x = 0, abs_x = vao_x * VAO_DIVISOR; x < tiles.x + 1; ++x, ++abs_x, ++i) {
97                         data[i].position = glm::vec3(x, GetElevation(abs_x, abs_z), z);
98                         data[i].normal = GetNormal(abs_x, abs_z);
99                 }
100         }
101         glUnmapBuffer(GL_ARRAY_BUFFER);
102 }
103
104 glm::vec3 Floor::GetNormal(int x, int z) const noexcept {
105         // TODO: not sure about the sign here
106         return normalize(glm::vec3(
107                 ClampedElevation(x - 1, z) - ClampedElevation(x + 1, z),
108                 2.0f,
109                 ClampedElevation(x, z - 1) - ClampedElevation(x, z + 1)
110         ));
111 }
112
113 glm::ivec2 Floor::Tiles(int vao_x, int vao_z) const noexcept {
114         return glm::ivec2(
115                 (unclean_width && vao_x == vao_width - 1) ? unclean_width : VAO_DIVISOR,
116                 (unclean_depth && vao_z == vao_depth - 1) ? unclean_depth : VAO_DIVISOR);
117 }
118
119 int Floor::NumTiles(int vao_x, int vao_z) const noexcept {
120         glm::ivec2 tiles = Tiles(vao_x, vao_z);
121         return tiles.x * tiles.y;
122 }
123
124 int Floor::NumVertices(int vao_x, int vao_z) const noexcept {
125         glm::ivec2 tiles = Tiles(vao_x, vao_z);
126         return (tiles.x + 1) * (tiles.y + 1);
127 }
128
129 void Floor::GenerateVertices() {
130         for (int z = 0; z < vao_depth; ++z) {
131                 for (int x = 0; x < vao_width; ++x) {
132                         FillAttribBuffer(x, z);
133                 }
134         }
135 }
136
137 void Floor::DrawVAO(int vao_x, int vao_z) const noexcept {
138         glBindVertexArray(vaos[vao_z * vao_width + vao_x]);
139         // TODO: this cries for triangle strips
140         glDrawElements(GL_TRIANGLES, NumTiles(vao_x, vao_z) * 6, GL_UNSIGNED_SHORT, nullptr);
141 }
142
143 }