]> git.localhorst.tv Git - tacos.git/blob - src/world/world.cpp
mouse cursor mockup
[tacos.git] / src / world / world.cpp
1 #include "Cursor.hpp"
2 #include "Floor.hpp"
3
4
5 namespace tacos {
6
7 Cursor::Cursor()
8 : vao(0)
9 , buffers{0}
10 , size(3)
11 , offset(0.1f)
12 , mode(HIDDEN) {
13         glGenVertexArrays(1, &vao);
14         glGenBuffers(2, buffers);
15
16         glBindVertexArray(vao);
17         glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
18         glEnableVertexAttribArray(0);
19         glVertexAttribPointer(0, 3, GL_FLOAT, 0, sizeof(Attributes), reinterpret_cast<const void *>(offsetof(Attributes, position)));
20         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
21         glBindVertexArray(0);
22 }
23
24 Cursor::~Cursor() {
25         glDeleteBuffers(2, buffers);
26         glDeleteVertexArrays(1, &vao);
27 }
28
29 void Cursor::Hide() noexcept {
30         mode = HIDDEN;
31 }
32
33 void Cursor::FloorTile(const Floor &floor, int tile_x, int tile_z) {
34         // TODO: only update if changed
35         mode = FLOOR;
36
37         int x_begin = glm::clamp(tile_x, 0, floor.Width() - size);
38         int x_end = x_begin + size;
39         int z_begin = glm::clamp(tile_z, 0, floor.Depth() - size);
40         int z_end = z_begin + size;
41
42         glBindVertexArray(vao);
43         glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
44         glBufferData(GL_ARRAY_BUFFER, size * size * sizeof(Attributes), nullptr, GL_DYNAMIC_DRAW);
45         Attributes *attrib = reinterpret_cast<Attributes *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
46         for (int z = z_begin, index = 0; z < z_end; ++z) {
47                 for (int x = x_begin; x < x_end; ++x, ++index) {
48                         attrib[index].position = glm::vec3(x, floor.GetElevation(x, z) + offset, z);
49                 }
50         }
51         glUnmapBuffer(GL_ARRAY_BUFFER);
52
53         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
54         glBufferData(GL_ELEMENT_ARRAY_BUFFER, (size - 1) * (size - 1) * 6, nullptr, GL_DYNAMIC_DRAW);
55         unsigned char *element = reinterpret_cast<unsigned char *>(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY));
56         for (int z = 0, index = 0; z < size - 1; ++z) {
57                 for (int x = 0; x < size - 1; ++x, ++index) {
58                         element[index * 6 + 0] = (z + 0) * size + (x + 0);
59                         element[index * 6 + 1] = (z + 0) * size + (x + 1);
60                         element[index * 6 + 2] = (z + 1) * size + (x + 0);
61                         element[index * 6 + 3] = (z + 0) * size + (x + 1);
62                         element[index * 6 + 4] = (z + 1) * size + (x + 1);
63                         element[index * 6 + 5] = (z + 1) * size + (x + 0);
64                 }
65         }
66         glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
67         glBindVertexArray(0);
68 }
69
70 void Cursor::Draw() const noexcept {
71         glBindVertexArray(vao);
72         glDrawElements(GL_TRIANGLES, (size - 1) * (size - 1) * 6, GL_UNSIGNED_BYTE, nullptr);
73 }
74
75
76 constexpr int Floor::VAO_DIVISOR;
77
78 Floor::Floor(int w, int d)
79 : width(w)
80 , depth(d)
81 , elevation(w * d)
82 , tile_width(width - 1)
83 , tile_depth(depth - 1)
84 , unclean_width(tile_width % VAO_DIVISOR)
85 , unclean_depth(tile_depth % VAO_DIVISOR)
86 , vao_width(tile_width / VAO_DIVISOR + bool(unclean_width))
87 , vao_depth(tile_depth / VAO_DIVISOR + bool(unclean_depth))
88 , vaos(vao_width * vao_depth)
89 , general_elements(vao_width * vao_depth)
90 , unclean_width_elements(general_elements + bool(unclean_width))
91 , unclean_depth_elements(unclean_depth ? (unclean_width_elements + 1) : general_elements)
92 , unclean_corner_elements(std::max(unclean_width_elements, unclean_depth_elements) + (unclean_width && unclean_depth))
93 , buffers(unclean_corner_elements + 1) {
94         glGenVertexArrays(vaos.size(), vaos.data());
95         glGenBuffers(buffers.size(), buffers.data());
96         int x_end = vao_width - bool(unclean_width);
97         int z_end = vao_depth - bool(unclean_depth);
98         for (int z = 0; z < z_end; ++z) {
99                 for (int x = 0; x < x_end; ++x) {
100                         SetupVAO(z * vao_width + x, buffers[general_elements], (VAO_DIVISOR + 1) * (VAO_DIVISOR + 1));
101                 }
102         }
103         // always fill general element buffer (there won't be one needed for maps with x or z less than divisor,
104         // but that shouldn't happen in practice, only during tests in which case I don't care about the overhead
105         FillElementBuffer(buffers[general_elements], VAO_DIVISOR, VAO_DIVISOR);
106         if (unclean_width) {
107                 for (int z = 0; z < z_end; ++z) {
108                         SetupVAO(z * vao_width + x_end, buffers[unclean_width_elements], (unclean_width + 1) * (VAO_DIVISOR + 1));
109                 }
110                 FillElementBuffer(buffers[unclean_width_elements], unclean_width, VAO_DIVISOR);
111         }
112         if (unclean_depth) {
113                 for (int x = 0; x < x_end; ++x) {
114                         SetupVAO(z_end * vao_width + x, buffers[unclean_depth_elements], (VAO_DIVISOR + 1) * (unclean_depth + 1));
115                 }
116                 FillElementBuffer(buffers[unclean_depth_elements], VAO_DIVISOR, unclean_depth);
117         }
118         if (unclean_width && unclean_depth) {
119                 SetupVAO(z_end * vao_width + x_end, buffers[unclean_corner_elements], (unclean_width + 1) * (unclean_depth + 1));
120                 FillElementBuffer(buffers[unclean_corner_elements], unclean_width, unclean_depth);
121         }
122 }
123
124 Floor::~Floor() noexcept {
125         glDeleteBuffers(buffers.size(), buffers.data());
126         glDeleteVertexArrays(vaos.size(), vaos.data());
127 }
128
129 void Floor::SetupVAO(int which, GLuint element_buffer, int vertex_count) noexcept {
130         glBindVertexArray(vaos[which]);
131         glBindBuffer(GL_ARRAY_BUFFER, buffers[which]);
132         glBufferData(GL_ARRAY_BUFFER, vertex_count * sizeof(Attributes), nullptr, GL_STATIC_DRAW);
133         glEnableVertexAttribArray(0);
134         glVertexAttribPointer(0, 3, GL_FLOAT, 0, sizeof(Attributes), reinterpret_cast<const void *>(offsetof(Attributes, position)));
135         glEnableVertexAttribArray(1);
136         glVertexAttribPointer(1, 3, GL_FLOAT, 0, sizeof(Attributes), reinterpret_cast<const void *>(offsetof(Attributes, normal)));
137         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);
138 }
139
140 void Floor::FillElementBuffer(GLuint which, int tile_width, int tile_depth) {
141         // unbind VAO so we don't accidentally trash an element buffer binding
142         glBindVertexArray(0);
143         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, which);
144         glBufferData(GL_ELEMENT_ARRAY_BUFFER, tile_width * tile_depth * sizeof(short) * 6, nullptr, GL_STATIC_DRAW);
145         // TODO: this can return null on error (out of memory in this case)
146         //       might be worth checking eventually
147         short *data = reinterpret_cast<short *>(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY));
148         for (int z = 0, i = 0; z < tile_depth; ++z) {
149                 for (int x = 0; x < tile_width; ++x, ++i) {
150                         data[i * 6 + 0] = (z + 0) * (tile_width + 1) + (x + 0);
151                         data[i * 6 + 1] = (z + 0) * (tile_width + 1) + (x + 1);
152                         data[i * 6 + 2] = (z + 1) * (tile_width + 1) + (x + 0);
153                         data[i * 6 + 3] = (z + 0) * (tile_width + 1) + (x + 1);
154                         data[i * 6 + 4] = (z + 1) * (tile_width + 1) + (x + 1);
155                         data[i * 6 + 5] = (z + 1) * (tile_width + 1) + (x + 0);
156                 }
157         }
158         glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
159 }
160
161 void Floor::FillAttribBuffer(int vao_x, int vao_z) {
162         glBindBuffer(GL_ARRAY_BUFFER, buffers[vao_z * vao_width + vao_x]);
163         Attributes *data = reinterpret_cast<Attributes *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
164         glm::ivec2 tiles(Tiles(vao_x, vao_z));
165         for (int z = 0, abs_z = vao_z * VAO_DIVISOR, i = 0; z < tiles.y + 1; ++z, ++abs_z) {
166                 for (int x = 0, abs_x = vao_x * VAO_DIVISOR; x < tiles.x + 1; ++x, ++abs_x, ++i) {
167                         data[i].position = glm::vec3(x, GetElevation(abs_x, abs_z), z);
168                         data[i].normal = GetNormal(abs_x, abs_z);
169                 }
170         }
171         glUnmapBuffer(GL_ARRAY_BUFFER);
172 }
173
174 glm::vec3 Floor::GetNormal(int x, int z) const noexcept {
175         // TODO: not sure about the sign here
176         return normalize(glm::vec3(
177                 ClampedElevation(x - 1, z) - ClampedElevation(x + 1, z),
178                 2.0f,
179                 ClampedElevation(x, z - 1) - ClampedElevation(x, z + 1)
180         ));
181 }
182
183 glm::ivec2 Floor::Tiles(int vao_x, int vao_z) const noexcept {
184         return glm::ivec2(
185                 (unclean_width && vao_x == vao_width - 1) ? unclean_width : VAO_DIVISOR,
186                 (unclean_depth && vao_z == vao_depth - 1) ? unclean_depth : VAO_DIVISOR);
187 }
188
189 int Floor::NumTiles(int vao_x, int vao_z) const noexcept {
190         glm::ivec2 tiles = Tiles(vao_x, vao_z);
191         return tiles.x * tiles.y;
192 }
193
194 int Floor::NumVertices(int vao_x, int vao_z) const noexcept {
195         glm::ivec2 tiles = Tiles(vao_x, vao_z);
196         return (tiles.x + 1) * (tiles.y + 1);
197 }
198
199 void Floor::GenerateVertices() {
200         for (int z = 0; z < vao_depth; ++z) {
201                 for (int x = 0; x < vao_width; ++x) {
202                         FillAttribBuffer(x, z);
203                 }
204         }
205 }
206
207 void Floor::DrawVAO(int vao_x, int vao_z) const noexcept {
208         glBindVertexArray(vaos[vao_z * vao_width + vao_x]);
209         // TODO: this cries for triangle strips
210         glDrawElements(GL_TRIANGLES, NumTiles(vao_x, vao_z) * 6, GL_UNSIGNED_SHORT, nullptr);
211 }
212
213 bool Floor::Intersection(const Ray &ray, glm::vec3 &point) {
214         // TODO: this tests for Y=0 plane intersection, change to respect heightmap
215         if (std::abs(ray.direction.y) < std::numeric_limits<float>::epsilon()) {
216                 // ray parallel to plane
217                 return false;
218         }
219         float factor = ray.origin.y / ray.direction.y;
220         if (factor > 0.0f) {
221                 // intersection "behind" the ray
222                 return false;
223         }
224         point.x = ray.origin.x - (ray.direction.x * factor);
225         point.y = 0.0f;
226         point.z = ray.origin.z - (ray.direction.z * factor);
227         return point.x >= 0.0f && point.x <= float(width) && point.z >= 0.0f && point.z <= float(depth);
228 }
229
230 }