X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fworld%2Fworld.cpp;h=03ff1975e4db620c3517d423a2e0e46a888f6d2d;hb=refs%2Fheads%2Fmaster;hp=25723ba81c052f21e7fbeda023518817fdb2c42f;hpb=2e761a60d1717297b7e308ef1b66e2a81319fdff;p=tacos.git diff --git a/src/world/world.cpp b/src/world/world.cpp index 25723ba..03ff197 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -1,29 +1,25 @@ #include "Cursor.hpp" #include "Floor.hpp" +#include "../graphics/buffer.hpp" + +#include +#include + 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(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(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(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 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(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 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(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY)); + MappedBuffer 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(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); + MappedBuffer 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::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; } }