--- /dev/null
+#ifndef TACOS_GRAPHICS_BUFFER_HPP_
+#define TACOS_GRAPHICS_BUFFER_HPP_
+
+#include "../app/error.hpp"
+
+#include <algorithm>
+#include <GL/glew.h>
+
+
+namespace tacos {
+
+template<class T>
+class MappedBuffer {
+
+public:
+ MappedBuffer(GLenum target, GLenum access)
+ : buf(reinterpret_cast<T *>(glMapBuffer(target, access)))
+ , target(target) {
+ if (!buf) {
+ throw GLError("failed to map buffer");
+ }
+ }
+ MappedBuffer()
+ : buf(nullptr)
+ , target(0) {
+ }
+ ~MappedBuffer() noexcept {
+ if (buf) {
+ glUnmapBuffer(target);
+ }
+ }
+
+ MappedBuffer(MappedBuffer<T> &&other) noexcept
+ : buf(other.buf)
+ , target(other.target) {
+ other.buf = nullptr;
+ }
+ MappedBuffer<T> &operator =(MappedBuffer<T> &&other) noexcept {
+ std::swap(buf, other.buf);
+ std::swap(target, other.target);
+ }
+
+ MappedBuffer(const MappedBuffer<T> &) = delete;
+ MappedBuffer<T> &operator =(const MappedBuffer<T> &) = delete;
+
+ explicit operator bool() const noexcept { return buf; }
+
+public:
+ T &operator [](std::size_t i) noexcept { return buf[i]; }
+ const T &operator [](std::size_t i) const noexcept { return buf[i]; }
+
+private:
+ T *buf;
+ GLenum target;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "gl_traits.hpp"
+
+
+namespace tacos {
+
+constexpr GLint gl_traits<signed char>::size;
+constexpr GLenum gl_traits<signed char>::type;
+
+constexpr GLint gl_traits<unsigned char>::size;
+constexpr GLenum gl_traits<unsigned char>::type;
+
+constexpr GLint gl_traits<short>::size;
+constexpr GLenum gl_traits<short>::type;
+
+constexpr GLint gl_traits<unsigned short>::size;
+constexpr GLenum gl_traits<unsigned short>::type;
+
+constexpr GLint gl_traits<int>::size;
+constexpr GLenum gl_traits<int>::type;
+
+constexpr GLint gl_traits<unsigned int>::size;
+constexpr GLenum gl_traits<unsigned int>::type;
+
+constexpr GLint gl_traits<float>::size;
+constexpr GLenum gl_traits<float>::type;
+
+constexpr GLint gl_traits<double>::size;
+constexpr GLenum gl_traits<double>::type;
+
+}
--- /dev/null
+#ifndef TACOS_GRAPHICS_GL_TRAITS_HPP_
+#define TACOS_GRAPHICS_GL_TRAITS_HPP_
+
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+
+
+namespace tacos {
+
+template<class T>
+struct gl_traits {
+
+ /// number of components per generic attribute
+ /// must be 1, 2, 3, 4
+ // static constexpr GLint size;
+
+ /// component type
+ /// accepted values are:
+ /// GL_BYTE, GL_UNSIGNED_BYTE,
+ /// GL_SHORT, GL_UNSIGNED_SHORT,
+ /// GL_INT, GL_UNSIGNED_INT,
+ /// GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE,
+ /// GL_FIXED, GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REV
+ // static constexpr GLenum type;
+
+};
+
+
+// basic types
+
+template<> struct gl_traits<signed char> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_BYTE;
+};
+
+template<> struct gl_traits<unsigned char> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_UNSIGNED_BYTE;
+};
+
+template<> struct gl_traits<short> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_SHORT;
+};
+
+template<> struct gl_traits<unsigned short> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_UNSIGNED_SHORT;
+};
+
+template<> struct gl_traits<int> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_INT;
+};
+
+template<> struct gl_traits<unsigned int> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_UNSIGNED_INT;
+};
+
+template<> struct gl_traits<float> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_FLOAT;
+};
+
+template<> struct gl_traits<double> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_DOUBLE;
+};
+
+// composite types
+
+template<>
+template<class T, glm::precision P>
+struct gl_traits<glm::tvec1<T, P>> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = gl_traits<T>::type;
+};
+template<class T, glm::precision P>
+constexpr GLint gl_traits<glm::tvec1<T, P>>::size;
+template<class T, glm::precision P>
+constexpr GLenum gl_traits<glm::tvec1<T, P>>::type;
+
+template<>
+template<class T, glm::precision P>
+struct gl_traits<glm::tvec2<T, P>> {
+ static constexpr GLint size = 2;
+ static constexpr GLenum type = gl_traits<T>::type;
+};
+template<class T, glm::precision P>
+constexpr GLint gl_traits<glm::tvec2<T, P>>::size;
+template<class T, glm::precision P>
+constexpr GLenum gl_traits<glm::tvec2<T, P>>::type;
+
+template<>
+template<class T, glm::precision P>
+struct gl_traits<glm::tvec3<T, P>> {
+ static constexpr GLint size = 3;
+ static constexpr GLenum type = gl_traits<T>::type;
+};
+template<class T, glm::precision P>
+constexpr GLint gl_traits<glm::tvec3<T, P>>::size;
+template<class T, glm::precision P>
+constexpr GLenum gl_traits<glm::tvec3<T, P>>::type;
+
+template<>
+template<class T, glm::precision P>
+struct gl_traits<glm::tvec4<T, P>> {
+ static constexpr GLint size = 4;
+ static constexpr GLenum type = gl_traits<T>::type;
+};
+template<class T, glm::precision P>
+constexpr GLint gl_traits<glm::tvec4<T, P>>::size;
+template<class T, glm::precision P>
+constexpr GLenum gl_traits<glm::tvec4<T, P>>::type;
+
+}
+
+#endif
--- /dev/null
+#ifndef TACOS_GRAPHICS_VAO_HPP_
+#define TACOS_GRAPHICS_VAO_HPP_
+
+#include "buffer.hpp"
+#include "gl_traits.hpp"
+
+#include <GL/glew.h>
+
+
+namespace tacos {
+
+/// Simple vertex array object based on indexed draw calls with attributes
+/// interleaved in a single buffer.
+template<class Attributes, class Element>
+class SimpleVAO {
+
+public:
+ SimpleVAO()
+ : vao(0)
+ , buffers{0} {
+ glGenVertexArrays(1, &vao);
+ glGenBuffers(2, buffers);
+ }
+ ~SimpleVAO() noexcept {
+ glDeleteBuffers(2, buffers);
+ glDeleteVertexArrays(1, &vao);
+ }
+
+ SimpleVAO(const SimpleVAO &) = delete;
+ SimpleVAO &operator =(const SimpleVAO &) = delete;
+
+public:
+ void Bind() const noexcept {
+ glBindVertexArray(vao);
+ }
+ void Unbind() const noexcept {
+ glBindVertexArray(0);
+ }
+ void BindAttributes() const noexcept {
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
+ }
+ void BindElements() const noexcept {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
+ }
+ void EnableAttribute(GLuint index) noexcept {
+ glEnableVertexAttribArray(index);
+ }
+ void DisableAttribute(GLuint index) noexcept {
+ glDisableVertexAttribArray(index);
+ }
+ template<class Attribute>
+ void AttributePointer(GLuint index, bool normalized, std::size_t offset) noexcept {
+ glVertexAttribPointer(
+ index,
+ gl_traits<Attribute>::size,
+ gl_traits<Attribute>::type,
+ normalized,
+ sizeof(Attributes),
+ reinterpret_cast<const void *>(offset)
+ );
+ }
+ void ReserveAttributes(std::size_t size, GLenum usage) noexcept {
+ glBufferData(GL_ARRAY_BUFFER, size * sizeof(Attributes), nullptr, usage);
+ }
+ MappedBuffer<Attributes> MapAttributes(GLenum access) {
+ return MappedBuffer<Attributes>(GL_ARRAY_BUFFER, access);
+ }
+ void ReserveElements(std::size_t size, GLenum usage) noexcept {
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, size * sizeof(Element), nullptr, usage);
+ }
+ MappedBuffer<Element> MapElements(GLenum access) {
+ return MappedBuffer<Element>(GL_ELEMENT_ARRAY_BUFFER, access);
+ }
+ void DrawTriangles(std::size_t size, std::size_t offset = 0) const noexcept {
+ glDrawElements(GL_TRIANGLES, size, gl_traits<Element>::type, ((Element *) nullptr) + offset);
+ }
+
+private:
+ GLuint vao;
+ GLuint buffers[2];
+
+};
+
+}
+
+#endif
#ifndef TACOS_WORLD_CURSOR_HPP_
#define TACOS_WORLD_CURSOR_HPP_
-#include <GL/glew.h>
-#include <glm/glm.hpp>
+#include "../graphics/vao.hpp"
namespace tacos {
public:
Cursor();
- ~Cursor();
-
- Cursor(const Cursor &) = delete;
- Cursor &operator =(const Cursor &) = delete;
/// hide cursor
void Hide() noexcept;
void Draw() const noexcept;
private:
- GLuint vao;
- GLuint buffers[2];
+ struct Attributes {
+ glm::vec3 position;
+ };
+
+ SimpleVAO<Attributes, unsigned char> vao;
// side length in vertices (make sure it's between 2 and 8 inclusive)
int size;
FLOOR,
} mode;
- struct Attributes {
- glm::vec3 position;
- };
-
};
}
#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 {