]> git.localhorst.tv Git - tacos.git/blobdiff - src/world/world.cpp
isolate some GL stuff
[tacos.git] / src / world / world.cpp
index 25723ba81c052f21e7fbeda023518817fdb2c42f..03ff1975e4db620c3517d423a2e0e46a888f6d2d 100644 (file)
@@ -1,29 +1,25 @@
 #include "Cursor.hpp"
 #include "Floor.hpp"
 
+#include "../graphics/buffer.hpp"
+
+#include <iostream>
+#include <glm/gtx/io.hpp>
+
 
 namespace tacos {
 
 Cursor::Cursor()
-: vao(0)
-, buffers{0}
+: vao()
 , size(3)
 , offset(0.1f)
 , mode(HIDDEN) {
-       glGenVertexArrays(1, &vao);
-       glGenBuffers(2, buffers);
-
-       glBindVertexArray(vao);
-       glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
-       glEnableVertexAttribArray(0);
-       glVertexAttribPointer(0, 3, GL_FLOAT, 0, sizeof(Attributes), reinterpret_cast<const void *>(offsetof(Attributes, position)));
-       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
-       glBindVertexArray(0);
-}
-
-Cursor::~Cursor() {
-       glDeleteBuffers(2, buffers);
-       glDeleteVertexArrays(1, &vao);
+       vao.Bind();
+       vao.BindAttributes();
+       vao.EnableAttribute(0);
+       vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
+       vao.BindElements();
+       vao.Unbind();
 }
 
 void Cursor::Hide() noexcept {
@@ -39,37 +35,39 @@ void Cursor::FloorTile(const Floor &floor, int tile_x, int tile_z) {
        int z_begin = glm::clamp(tile_z, 0, floor.Depth() - size);
        int z_end = z_begin + size;
 
-       glBindVertexArray(vao);
-       glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
-       glBufferData(GL_ARRAY_BUFFER, size * size * sizeof(Attributes), nullptr, GL_DYNAMIC_DRAW);
-       Attributes *attrib = reinterpret_cast<Attributes *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
-       for (int z = z_begin, index = 0; z < z_end; ++z) {
-               for (int x = x_begin; x < x_end; ++x, ++index) {
-                       attrib[index].position = glm::vec3(x, floor.GetElevation(x, z) + offset, z);
+       vao.Bind();
+       vao.BindAttributes();
+       vao.ReserveAttributes(size * size, GL_DYNAMIC_DRAW);
+       {
+               MappedBuffer<Attributes> attrib(vao.MapAttributes(GL_WRITE_ONLY));
+               for (int z = z_begin, index = 0; z < z_end; ++z) {
+                       for (int x = x_begin; x < x_end; ++x, ++index) {
+                               attrib[index].position = glm::vec3(x, floor.GetElevation(x, z) + offset, z);
+                       }
                }
        }
-       glUnmapBuffer(GL_ARRAY_BUFFER);
-
-       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
-       glBufferData(GL_ELEMENT_ARRAY_BUFFER, (size - 1) * (size - 1) * 6, nullptr, GL_DYNAMIC_DRAW);
-       unsigned char *element = reinterpret_cast<unsigned char *>(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY));
-       for (int z = 0, index = 0; z < size - 1; ++z) {
-               for (int x = 0; x < size - 1; ++x, ++index) {
-                       element[index * 6 + 0] = (z + 0) * size + (x + 0);
-                       element[index * 6 + 1] = (z + 0) * size + (x + 1);
-                       element[index * 6 + 2] = (z + 1) * size + (x + 0);
-                       element[index * 6 + 3] = (z + 0) * size + (x + 1);
-                       element[index * 6 + 4] = (z + 1) * size + (x + 1);
-                       element[index * 6 + 5] = (z + 1) * size + (x + 0);
+
+       vao.BindElements();
+       vao.ReserveElements((size - 1) * (size - 1) * 6, GL_DYNAMIC_DRAW);
+       {
+               MappedBuffer<unsigned char> element(vao.MapElements(GL_WRITE_ONLY));
+               for (int z = 0, index = 0; z < size - 1; ++z) {
+                       for (int x = 0; x < size - 1; ++x, ++index) {
+                               element[index * 6 + 0] = (z + 0) * size + (x + 0);
+                               element[index * 6 + 1] = (z + 0) * size + (x + 1);
+                               element[index * 6 + 2] = (z + 1) * size + (x + 0);
+                               element[index * 6 + 3] = (z + 0) * size + (x + 1);
+                               element[index * 6 + 4] = (z + 1) * size + (x + 1);
+                               element[index * 6 + 5] = (z + 1) * size + (x + 0);
+                       }
                }
        }
-       glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
-       glBindVertexArray(0);
+       vao.Unbind();
 }
 
 void Cursor::Draw() const noexcept {
-       glBindVertexArray(vao);
-       glDrawElements(GL_TRIANGLES, (size - 1) * (size - 1) * 6, GL_UNSIGNED_BYTE, nullptr);
+       vao.Bind();
+       vao.DrawTriangles((size - 1) * (size - 1) * 6);
 }
 
 
@@ -142,9 +140,7 @@ void Floor::FillElementBuffer(GLuint which, int tile_width, int tile_depth) {
        glBindVertexArray(0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, which);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, tile_width * tile_depth * sizeof(short) * 6, nullptr, GL_STATIC_DRAW);
-       // TODO: this can return null on error (out of memory in this case)
-       //       might be worth checking eventually
-       short *data = reinterpret_cast<short *>(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY));
+       MappedBuffer<short> data(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
        for (int z = 0, i = 0; z < tile_depth; ++z) {
                for (int x = 0; x < tile_width; ++x, ++i) {
                        data[i * 6 + 0] = (z + 0) * (tile_width + 1) + (x + 0);
@@ -155,12 +151,11 @@ void Floor::FillElementBuffer(GLuint which, int tile_width, int tile_depth) {
                        data[i * 6 + 5] = (z + 1) * (tile_width + 1) + (x + 0);
                }
        }
-       glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
 }
 
 void Floor::FillAttribBuffer(int vao_x, int vao_z) {
        glBindBuffer(GL_ARRAY_BUFFER, buffers[vao_z * vao_width + vao_x]);
-       Attributes *data = reinterpret_cast<Attributes *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
+       MappedBuffer<Attributes> data(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
        glm::ivec2 tiles(Tiles(vao_x, vao_z));
        for (int z = 0, abs_z = vao_z * VAO_DIVISOR, i = 0; z < tiles.y + 1; ++z, ++abs_z) {
                for (int x = 0, abs_x = vao_x * VAO_DIVISOR; x < tiles.x + 1; ++x, ++abs_x, ++i) {
@@ -168,7 +163,6 @@ void Floor::FillAttribBuffer(int vao_x, int vao_z) {
                        data[i].normal = GetNormal(abs_x, abs_z);
                }
        }
-       glUnmapBuffer(GL_ARRAY_BUFFER);
 }
 
 glm::vec3 Floor::GetNormal(int x, int z) const noexcept {
@@ -211,20 +205,113 @@ void Floor::DrawVAO(int vao_x, int vao_z) const noexcept {
 }
 
 bool Floor::Intersection(const Ray &ray, glm::vec3 &point) {
-       // TODO: this tests for Y=0 plane intersection, change to respect heightmap
-       if (std::abs(ray.direction.y) < std::numeric_limits<float>::epsilon()) {
-               // ray parallel to plane
-               return false;
+       // see http://www.cse.yorku.ca/~amana/research/grid.pdf and
+       // http://www.flipcode.com/archives/Raytracing_Topics_Techniques-Part_4_Spatial_Subdivisions.shtml section Grid Traversal
+
+       // cache 1/dir to avoid some conditionals and divisions
+       glm::vec3 inverse_direction(ray.InverseDirection());
+
+       // cell indicates the current tile we're considering
+       glm::ivec2 cell(int(ray.origin.x), int(ray.origin.z));
+
+       // holds the distance along the ray to advance by one cell in each direction
+       glm::vec3 tDelta(glm::abs(inverse_direction));
+       // holds the distance along the ray to the next cell boundary
+       // TODO: not sure if that is always correct (e.g. with negative components in ray direction)
+       glm::vec3 tMax(tDelta * (1.0f - glm::fract(ray.origin)));
+
+       // if ray's origin is outside the grid, advance to the first cell it hits
+       float x_near, x_far, z_near, z_far, t_min, t_max;
+       if (cell.x < 0 || cell.x >= width || cell.y < 0 || cell.y >= depth) {
+               x_near = (-ray.origin.x) * inverse_direction.x;
+               // subtracting one so the point is in the last cell, rather than after it, when approaching from the far end
+               x_far = (width - 1 - ray.origin.x) * inverse_direction.x;
+               z_near = (-ray.origin.z) * inverse_direction.z;
+               z_far = (depth - 1 - ray.origin.z) * inverse_direction.z;
+               t_min = std::max(std::min(x_near, x_far), std::min(z_near, z_far));
+               t_max = std::min(std::max(x_near, x_far), std::max(z_near, z_far));
+               if (t_max < 0.0f || t_min > t_max) {
+                       // ray doesn't touch our grid at all
+                       return false;
+               }
+               glm::vec3 contact = ray.origin + t_min * ray.direction;
+               cell.x = int(contact.x);
+               cell.y = int(contact.z);
+               // TODO: not sure if this is correct, could be that contact has to be recalculated with
+               //       outer planes instead of the -1 ones (that might actually also apply to cell calculation)
+               tMax.x = (float(cell.x) - contact.x) * inverse_direction.x;
+               tMax.z = (float(cell.y) - contact.z) * inverse_direction.z;
        }
-       float factor = ray.origin.y / ray.direction.y;
-       if (factor > 0.0f) {
-               // intersection "behind" the ray
+
+       // step hold the direction we're traversing the grid
+       glm::ivec2 step(glm::sign(ray.direction.x), glm::sign(ray.direction.z));
+
+       if (step.x == 0 && step.y == 0) {
+               // ray shoots straight up or down
+               // check the current cell (if it's valid)
+               if (cell.x >= 0 && cell.x < width && cell.y >= 0 && cell.y < depth) {
+                       if (TriangleIntersection(
+                               ray,
+                               glm::vec3(float(cell.x + 0), GetElevation(cell.x + 0, cell.y + 0), float(cell.y + 0)),
+                               glm::vec3(float(cell.x + 1), GetElevation(cell.x + 1, cell.y + 0), float(cell.y + 0)),
+                               glm::vec3(float(cell.x + 0), GetElevation(cell.x + 0, cell.y + 1), float(cell.y + 1)),
+                               point
+                       )) {
+                               return true;
+                       }
+                       if (TriangleIntersection(
+                               ray,
+                               glm::vec3(float(cell.x + 1), GetElevation(cell.x + 1, cell.y + 0), float(cell.y + 0)),
+                               glm::vec3(float(cell.x + 1), GetElevation(cell.x + 1, cell.y + 1), float(cell.y + 1)),
+                               glm::vec3(float(cell.x + 0), GetElevation(cell.x + 0, cell.y + 1), float(cell.y + 1)),
+                               point
+                       )) {
+                               return true;
+                       }
+               }
                return false;
        }
-       point.x = ray.origin.x - (ray.direction.x * factor);
-       point.y = 0.0f;
-       point.z = ray.origin.z - (ray.direction.z * factor);
-       return point.x >= 0.0f && point.x <= float(width) && point.z >= 0.0f && point.z <= float(depth);
+
+       // cache for the height of the vertices of the current cell
+       float height[4];
+
+       while (cell.x >= 0 && cell.x < width && cell.y >= 0 && cell.y < depth) {
+               // pull heights for the current cell
+               height[0] = GetElevation(cell.x + 0, cell.y + 0);
+               height[1] = GetElevation(cell.x + 1, cell.y + 0);
+               height[2] = GetElevation(cell.x + 0, cell.y + 1);
+               height[3] = GetElevation(cell.x + 1, cell.y + 1);
+               // the triangles used for rendering are (x,z), (x+1,z), (x,z+1) and
+               // (x+1,z),(x+1,z+1), (x,z+1), so height indices 012 and 132
+               if (TriangleIntersection(
+                       ray,
+                       glm::vec3(float(cell.x + 0), height[0], float(cell.y + 0)),
+                       glm::vec3(float(cell.x + 1), height[1], float(cell.y + 0)),
+                       glm::vec3(float(cell.x + 0), height[2], float(cell.y + 1)),
+                       point
+               )) {
+                       return true;
+               }
+               if (TriangleIntersection(
+                       ray,
+                       glm::vec3(float(cell.x + 1), height[1], float(cell.y + 0)),
+                       glm::vec3(float(cell.x + 1), height[3], float(cell.y + 1)),
+                       glm::vec3(float(cell.x + 0), height[2], float(cell.y + 1)),
+                       point
+               )) {
+                       return true;
+               }
+               // advance to the next cell
+               if (tMax.x < tMax.z) {
+                       tMax.x += tDelta.x;
+                       cell.x += step.x;
+               } else {
+                       tMax.z += tDelta.z;
+                       cell.y += step.y;
+               }
+       }
+
+       return false;
 }
 
 }