#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 {
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);
}
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);
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) {
data[i].normal = GetNormal(abs_x, abs_z);
}
}
- glUnmapBuffer(GL_ARRAY_BUFFER);
}
glm::vec3 Floor::GetNormal(int x, int z) const noexcept {
}
bool Floor::Intersection(const Ray &ray, glm::vec3 &point) {
- // see http://www.flipcode.com/archives/Raytracing_Topics_Techniques-Part_4_Spatial_Subdivisions.shtml section Grid Traversal
-
- // TODO: somehow this is not reliable at all, maybe due to numeric inaccuracy
- // the result is determined by checking the ray against triangles and it's
- // possible that it sometimes slips through the theoratically inexistant seams
+ // 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));
- // store the previous height to check against the lower of cell entry and exit
- float prev_height = ray.origin.y;
+ // 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;
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
- //std::cout << " ray does not touch grid" << std::endl;
return false;
}
glm::vec3 contact = ray.origin + t_min * ray.direction;
cell.x = int(contact.x);
cell.y = int(contact.z);
- prev_height = contact.y;
+ // 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;
}
// step hold the direction we're traversing the grid
return true;
}
}
- //std::cout << " ray is vertical and outside of grid" << std::endl;
return false;
}
// cache for the height of the vertices of the current cell
float height[4];
- // now step through each cell until Y gets below the surface or one of X or Z exit the grid bounds
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);
- // highest point in the cell
- float max_height = std::max(std::max(height[0], height[1]), std::max(height[2], height[3]));
-
- // check where the ray exits the current cell
- // test how far away the ray is from each plane and choose the closest
- x_near = (float(cell.x + step.x) - ray.origin.x) * inverse_direction.x;
- z_near = (float(cell.y + step.y) - ray.origin.z) * inverse_direction.z;
- // if dir is 0, inverse dir is infinity. multiplying 0 by infinity is NaN. min(x, inf) is x, min(x, nan) is x
- t_min = std::min(x_near, z_near);
- // heightof the ray at exit
- float cur_height = ray.origin.y + (t_min * ray.direction.y);
- // lowest point of the ray in the cell
- float ray_low = std::min(prev_height, cur_height);
- // store exit height for next cell's entry height
- prev_height = cur_height;
-
- // check if we might end up below the surface
- // if this is true, there still could be no intersection if the ray is close to parallel to the surface
- // or due to precision issues, which are currently biting me
- if (ray_low < max_height) {
- // possibly, so check individual surfaces
- // 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;
- }
- // hmm, maybe I should check against planes and if true test if the XZ of the intersection points
- // lie within their corresponding half-square with some flexibility and somehow pick the right one
- //std::cout << " ray got below max floor height at cell " << cell << " but did not intersect a triangle" << std::endl;
+ // 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;
}
- // okay, we're still above, advance to the next cell
- if (x_near < z_near || std::isnan(z_near)) {
+ // 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;
}
}
- //std::cout << " ray left grid at cell " << cell << std::endl;
- // we left the grid, so no intersection
+
return false;
}